//----------------------------------------------------------------------------
//
// Copyright (c) 2002-2010 Microsoft Corporation. 
//
// This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
// copy of the license can be found in the License.html file at the root of this distribution. 
// By using this source code in any fashion, you are agreeing to be bound 
// by the terms of the Apache License, Version 2.0.
//
// You must not remove this notice, or any other, from this software.
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// Open up the compiler as an incremental service for lexing, parsing,
// type checking and intellisense-like environment-reporting.
//--------------------------------------------------------------------------


namespace Microsoft.FSharp.Compiler.SourceCodeServices

open Internal.Utilities
open System
open System.IO
open System.Text
open System.Threading
open System.Collections.Generic
 
open Microsoft.FSharp.Core.Printf
open Microsoft.FSharp.Compiler.AbstractIL
open Microsoft.FSharp.Compiler.AbstractIL.Internal  
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library  
open Microsoft.FSharp.Compiler 
open Microsoft.FSharp.Compiler.MSBuildResolver
open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics 
open Microsoft.FSharp.Compiler.PrettyNaming
open Internal.Utilities.Collections
open Internal.Utilities.Debug
open System.Security.Permissions

open Microsoft.FSharp.Compiler.Env 
open Microsoft.FSharp.Compiler.Parser
open Microsoft.FSharp.Compiler.Range
open Microsoft.FSharp.Compiler.Ast
open Microsoft.FSharp.Compiler.ErrorLogger
open Microsoft.FSharp.Compiler.Lexhelp
open Microsoft.FSharp.Compiler.Build
open Microsoft.FSharp.Compiler.Tast
open Microsoft.FSharp.Compiler.Tastops
open Microsoft.FSharp.Compiler.Tastops.DebugPrint
open Microsoft.FSharp.Compiler.Lib
open Microsoft.FSharp.Compiler.AbstractIL.IL 
open Microsoft.FSharp.Compiler.Layout
open Microsoft.FSharp.Compiler.TypeChecker
open Microsoft.FSharp.Compiler.Infos
open Microsoft.FSharp.Compiler.Nameres
open Internal.Utilities.StructuredFormat

/// Methods for dealing with F# sources files.
module internal SourceFile =
    /// Source file extensions
    let private compilableExtensions = Build.sigSuffixes @ Build.implSuffixes @ Build.scriptSuffixes
    /// Single file projects extensions
    let private singleFileProjectExtensions = Build.scriptSuffixes
    /// Whether or not this file is compilable
    let IsCompilable file =
        let ext = Path.GetExtension(file)
        compilableExtensions |> List.exists(fun e->0 = String.Compare(e,ext,StringComparison.OrdinalIgnoreCase))
    /// Whether or not this file should be a single-file project
    let MustBeSingleFileProject file =
        let ext = Path.GetExtension(file)
        singleFileProjectExtensions |> List.exists(fun e-> 0 = String.Compare(e,ext,StringComparison.OrdinalIgnoreCase))
    /// Whether or not this is an fsi generated by goto metadata.
    let IsFSharpLanguageServiceGenerated s =
        let genExtns = [ ".fsi" ] 
        let extn     = Path.GetExtension s
        if List.mem extn genExtns then
            let dirName  = Path.GetDirectoryName s
            dirName = FsiGeneration.PathForGeneratedVisualStudioFSharpTempFiles        
        else false
    /// Additonal #defines that should be in place when editing a file in a file editor
    /// such as VS.
    let AdditionalDefinesForUseInEditor(filename) =
        if Build.IsScript(filename) then ["INTERACTIVE";"EDITING"] // This is still used by the foreground parse
        else ["COMPILED";"EDITING"]
    
       
/// This corresponds to a token categorization originally used in Visual Studio 2003.
/// 
/// NOTE: This corresponds to a token categorization originally used in Visual Studio 2003 and the original Babel source code.
/// It is not clear it is a primary logical classification that should be being used in the 
/// more recent language service work.
type TokenColorKind =
      Default = 0
    | Text = 0
    | Keyword = 1
    | Comment = 2
    | Identifier = 3
    | String = 4
    | UpperIdentifier = 5
    | InactiveCode = 7
    | PreprocessorKeyword = 8
    | Number = 9
    | Operator = 10

/// Categorize an action the editor should take in respons to a token, e.g. brace matching
/// 
/// NOTE: This corresponds to a token categorization originally used in Visual Studio 2003 and the original Babel source code.
/// It is not clear it is a primary logical classification that should be being used in the 
/// more recent language service work.
type TriggerClass =
      None         = 0x00000000
    | MemberSelect = 0x00000001
    | MatchBraces  = 0x00000002 
    | ChoiceSelect = 0x00000004
    | MethodTip    = 0x000000F0
    | ParamStart   = 0x00000010
    | ParamNext    = 0x00000020
    | ParamEnd     = 0x00000040
    
    
/// This corresponds to a token categorization originally used in Visual Studio 2003.
/// 
/// NOTE: This corresponds to a token categorization originally used in Visual Studio 2003 and the original Babel source code.
/// It is not clear it is a primary logical classification that should be being used in the 
/// more recent language service work.
type TokenCharKind = 
      Default     = 0x00000000
    | Text        = 0x00000000
    | Keyword     = 0x00000001
    | Identifier  = 0x00000002
    | String      = 0x00000003
    | Literal     = 0x00000004
    | Operator    = 0x00000005
    | Delimiter   = 0x00000006
    | WhiteSpace  = 0x00000008
    | LineComment = 0x00000009
    | Comment     = 0x0000000A    


/// Information about a particular token from the tokenizer
type TokenInformation = {
    LeftColumn:int;
    RightColumn:int;
    ColorClass:TokenColorKind;
    CharClass:TokenCharKind;
    TriggerClass:TriggerClass;
    Tag:int
    TokenName:string }

//----------------------------------------------------------------------------
// Flags
//--------------------------------------------------------------------------

module internal Flags = 
#if DEBUG
    let loggingTypes             = System.Environment.GetEnvironmentVariable("mFSharp_Logging")
    let logging                  = not (String.IsNullOrEmpty(loggingTypes))
    let initialLoggingGUITypes   = loggingTypes
    let loggingGUI               = not (String.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("mFSharp_LogToWinForm")))
    let loggingStdOut            = not (String.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("mFSharp_LogToStdOut")))
#else
    let loggingTypes             = ""
    let logging                  = false
    let initialLoggingGUITypes   = ""
    let loggingGUI               = false
    let loggingStdOut            = false
#endif
    let doInit = 
        if logging && not loggingGUI  && not loggingStdOut then  
            let logFile = ("c:\\fsharp\\log-m"+System.AppDomain.CurrentDomain.FriendlyName+".log") 
            let traceFile = ("c:\\fsharp\\trace-m"+System.AppDomain.CurrentDomain.FriendlyName+".txt") 
            try
                let log = (File.CreateText logFile  :> TextWriter)
                setDiagnosticsChannel(Some(log));
                progress := true;
            with e->
                // Don't kill the language service just because we couldn't log.
                System.Diagnostics.Debug.Assert(false, e.ToString())                
                ()
            if logging then 
                dprintf "Opened log file %s for ML, config follows\n" logFile
                dprintf "logging types = %s\n" loggingTypes
            Trace.Log <- loggingTypes
            Trace.Out <- 
                try 
                    new StreamWriter(traceFile,append=false,encoding=System.Text.Encoding.UTF8) :> TextWriter
                with e -> 
                    // Don't kill the language service just because we couldn't log.
                    System.Diagnostics.Debug.Assert(false, e.ToString())                
                    System.Console.Out 
                    
        elif loggingStdOut then 
            Trace.Log <- initialLoggingGUITypes
            Trace.Out <- System.Console.Out
#if GUI_LOGGING
        elif loggingGUI then 
            let f = new System.Windows.Forms.Form(Visible=true,TopMost=true,Width=600,Height=600)
            let memoryText = new System.Windows.Forms.TextBox(Text = "?? Kb", Width = 200)
            let memoryButton = new System.Windows.Forms.Button(Text = "GC and update Mem", Left = 200)
            memoryButton.Click.AddHandler(fun _ _ -> 
                            GC.Collect()
                            GC.WaitForPendingFinalizers()
                            memoryText.Text <- sprintf "%d Kb" (GC.GetTotalMemory(false) / 1024L)
                        )
            f.Controls.Add(memoryText)
            f.Controls.Add(memoryButton)            
            let rb = new System.Windows.Forms.RichTextBox(Dock=System.Windows.Forms.DockStyle.Fill, Font=new System.Drawing.Font("courier new",8.0f), Top = memoryButton.Height)
            f.Controls.Add(rb)
            rb.DoubleClick.Add(fun _ -> rb.Clear())
            let lab = new System.Windows.Forms.Label(Dock=System.Windows.Forms.DockStyle.Top, Font=new System.Drawing.Font("courier new",8.0f))
            f.Controls.Add(lab)
            let tb = new System.Windows.Forms.TextBox(Text=initialLoggingGUITypes,Height=10,Multiline=false,Dock=System.Windows.Forms.DockStyle.Top, Font=new System.Drawing.Font("courier new",8.0f))
            f.Controls.Add(tb)
            tb.TextChanged.Add (fun _ -> Trace.Log <- tb.Text) 
            
            let log = 
                let addTextOnGuiThread text = 
                    if not rb.IsDisposed then 
                        rb.AppendText(text); 
                        if text.Contains "\n" then 
                            rb.ScrollToCaret();
                            if rb.TextLength > 200000 then 
                               let s = rb.Text
                               rb.Text <- s.[s.Length - 100000..s.Length-1]
                let addText text = 
                    if f.InvokeRequired then 
                        f.BeginInvoke(new System.Windows.Forms.MethodInvoker(fun () -> addTextOnGuiThread text)) |> ignore
                    else
                        addTextOnGuiThread text
                    
                { new System.IO.TextWriter() with 
                      member x.Write(c:char) = addText (string c)
                      member x.Write(s:string) =  addText  s
                      member x.Encoding = System.Text.Encoding.Unicode } 
            setDiagnosticsChannel(Some(log));
            Trace.Log <- if initialLoggingGUITypes <> null then initialLoggingGUITypes else ""
            Trace.Out <- log
#endif
        else 
            // Would be nice to leave this at whatever channel was originally assigned.
            // This currently defeats NUnit's ability to capture logging output.
            setDiagnosticsChannel(None) (* VS does not support stderr! *)

    //let stripFSharpCoreReferences   = not (String.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("mFSharp_StripFSharpCoreReferences")))
    let GetEnvInteger e dflt = match System.Environment.GetEnvironmentVariable(e) with null -> dflt | t -> try int t with _ -> dflt
    let buildCacheSize   = GetEnvInteger "mFSharp_BuildCacheSize" 3
    let recentForgroundTypeCheckLookupSize = GetEnvInteger "mFSharp_RecentForegroundTypeCheckCacheSize" 5
    let braceMatchCacheSize = GetEnvInteger "mFSharp_BraceMatchCacheSize" 5
    let getDataTipTextCache = GetEnvInteger "mFSharp_GetDataTipTextCache" 20
    let maxErrorsOutOfProjectContext = GetEnvInteger "mFSharp_MaxErrorsOutOfProjectContext" 3
    let init() = doInit
        
open Flags



//----------------------------------------------------------------------------
// Babel flags
//--------------------------------------------------------------------------

module internal TokenClassifications = 

    //----------------------------------------------------------------------------
    //From tokens to flags  
    //--------------------------------------------------------------------------

    let tokenInfo token = 
        match token with 
        | IDENT s
          -> 
            if s.Length <= 0 then 
                System.Diagnostics.Debug.Assert(false, "BUG:Received zero length IDENT token.")
                // This is related to 4783. Recover by treating as lower case identifier.
                (TokenColorKind.Identifier,TokenCharKind.Identifier,TriggerClass.None)  
            else 
                if System.Char.ToUpperInvariant s.[0] = s.[0] then
                  (TokenColorKind.UpperIdentifier,TokenCharKind.Identifier,TriggerClass.None)
                else
                  (TokenColorKind.Identifier,TokenCharKind.Identifier,TriggerClass.None)  

        | DECIMAL _
        | BIGNUM _ | INT8 _  | UINT8 _ | INT16 _  | UINT16 _ | INT32 _ | UINT32 _ | INT64 _ | UINT64 _ 
        | UNATIVEINT _ | NATIVEINT _ | IEEE32 _ |  IEEE64 _
          -> (TokenColorKind.Number,TokenCharKind.Literal,TriggerClass.None)

        | INT32_DOT_DOT _ 
          // This will color the whole "1.." expression in a 'number' color 
          // (this isn't entirely correct, but it'll work for now - see bug 3727)
          -> (TokenColorKind.Number,TokenCharKind.Operator,TriggerClass.None)
        
        | INFIX_STAR_DIV_MOD_OP ("mod"  | "land" |  "lor" | "lxor")
        | INFIX_STAR_STAR_OP ("lsl" | "lsr" | "asr")
          -> (TokenColorKind.Keyword,TokenCharKind.Keyword,TriggerClass.None)

        | LPAREN_STAR_RPAREN
        | DOLLAR | INFIX_STAR_STAR_OP _ | INFIX_COMPARE_OP _ | COLON_GREATER  | COLON_COLON  
        | PERCENT_OP _ | INFIX_AT_HAT_OP _ | INFIX_BAR_OP _ | PLUS_MINUS_OP _ | PREFIX_OP _ | COLON_QMARK_GREATER   
        | INFIX_STAR_DIV_MOD_OP _ | INFIX_AMP_OP _ | AMP   | AMP_AMP  | BAR_BAR  | LESS  _ | GREATER _ | QMARK | QMARK_QMARK | COLON_QMARK
        | DOT_DOT       | QUOTE   | STAR  | HIGH_PRECEDENCE_TYAPP 
        | COLON    | COLON_EQUALS   | LARROW   | EQUALS | RQUOTE_DOT _
        | MINUS | ADJACENT_PREFIX_OP _   | FUNKY_OPERATOR_NAME _

          -> (TokenColorKind.Operator,TokenCharKind.Operator,TriggerClass.None)

        | COMMA
          -> (TokenColorKind.Text,TokenCharKind.Delimiter,TriggerClass.None)
              
        | DOT 
          -> (TokenColorKind.Operator,TokenCharKind.Delimiter,TriggerClass.MemberSelect)
              
        | BAR
          -> (TokenColorKind.Text,TokenCharKind.Delimiter,TriggerClass.None (* TriggerClass.ChoiceSelect *))
              
        | HASH | UNDERSCORE   
        | SEMICOLON    | SEMICOLON_SEMICOLON
          -> (TokenColorKind.Text,TokenCharKind.Delimiter,TriggerClass.None)
              
        | LPAREN
          // We need 'ParamStart' to trigger the 'GetDeclarations' method to show param info automatically
          // this is needed even if we don't use MPF for determining information about params
          -> (TokenColorKind.Text,TokenCharKind.Delimiter, TriggerClass.ParamStart ||| TriggerClass.MatchBraces)
              
        | RPAREN
          -> (TokenColorKind.Text,TokenCharKind.Delimiter,(* TriggerClass.ParamEnd; ||| *) TriggerClass.MatchBraces )
              
        | LBRACK_LESS  | LBRACE_LESS
          -> (TokenColorKind.Text,TokenCharKind.Delimiter,TriggerClass.None )
          
        | LQUOTE _  | LBRACK  | LBRACE | LBRACK_BAR 
          -> (TokenColorKind.Text,TokenCharKind.Delimiter,TriggerClass.MatchBraces )
          
        | GREATER_RBRACE   | GREATER_RBRACK  | GREATER_BAR_RBRACK
          -> (TokenColorKind.Text,TokenCharKind.Delimiter,TriggerClass.None )

        | RQUOTE _  | RBRACK  | RBRACE | BAR_RBRACK   
          -> (TokenColorKind.Text,TokenCharKind.Delimiter,TriggerClass.MatchBraces )
              
        | PUBLIC | PRIVATE | INTERNAL | BASE | GLOBAL
        | CONSTRAINT | INSTANCE | DELEGATE | INHERIT|CONSTRUCTOR|DEFAULT|OVERRIDE|ABSTRACT|CLASS
        | MEMBER | STATIC | NAMESPACE
        | OASSERT | OLAZY | ODECLEND | OBLOCKSEP | OEND | OBLOCKBEGIN | ORIGHT_BLOCK_END | OBLOCKEND | OTHEN | OELSE | OLET(_) | OBINDER _ | BINDER _ | ODO | OWITH | OFUNCTION | OFUN | ORESET | ODUMMY _ | DO_BANG | ODO_BANG | YIELD _ | YIELD_BANG  _ | OINTERFACE_MEMBER
        | ELIF | RARROW | SIG | STRUCT 
        | UPCAST   | DOWNCAST   | NULL   | RESERVED    | MODULE    | AND    | AS   | ASSERT   | ASR
        | DOWNTO   | EXCEPTION   | FALSE   | FOR   | FUN   | FUNCTION
        | FINALLY   | LAZY   | MATCH  | MUTABLE   | NEW   | OF    | OPEN   | OR | VOID | EXTERN
        | INTERFACE | REC   | TO   | TRUE   | TRY   | TYPE   |  VAL   | INLINE   | WHEN  | WHILE   | WITH
        | IF | THEN  | ELSE | DO | DONE | LET(_) | IN (*| NAMESPACE*)
        | HIGH_PRECEDENCE_PAREN_APP
        | HIGH_PRECEDENCE_BRACK_APP
          -> (TokenColorKind.Keyword,TokenCharKind.Keyword,TriggerClass.None)
              
        | BEGIN  
          -> (TokenColorKind.Keyword,TokenCharKind.Keyword,TriggerClass.None)

        | END 
          -> (TokenColorKind.Keyword,TokenCharKind.Keyword,TriggerClass.None)
        | HASH_LIGHT _
        | HASH_LINE _
        | HASH_IF _
        | HASH_ELSE _
        | HASH_ENDIF _ -> 
            (TokenColorKind.PreprocessorKeyword,TokenCharKind.WhiteSpace,TriggerClass.None)
        | INACTIVECODE _ -> 
            (TokenColorKind.InactiveCode,TokenCharKind.WhiteSpace,TriggerClass.None)
          

        | LEX_FAILURE _
        | WHITESPACE _ -> 
            (TokenColorKind.Default,TokenCharKind.WhiteSpace,TriggerClass.None)

        | COMMENT _ -> 
            (TokenColorKind.Comment,TokenCharKind.Comment,TriggerClass.None)
        | LINE_COMMENT _ -> 
            (TokenColorKind.Comment,TokenCharKind.LineComment,TriggerClass.None)
        | STRING_TEXT _ -> 
            (TokenColorKind.String,TokenCharKind.String,TriggerClass.None)
        | KEYWORD_STRING _ -> 
           (TokenColorKind.Keyword,TokenCharKind.Keyword,TriggerClass.None)
        | BYTEARRAY _ | STRING  _
        | CHAR _ (* bug://2863 asks to color 'char' as "string" *)
          -> (TokenColorKind.String,TokenCharKind.String,TriggerClass.None)
        | EOF _ -> failwith "tokenInfo"

module internal TestExpose = 
  let TokenInfo tok = TokenClassifications.tokenInfo tok
  
    //----------------------------------------------------------------------------
    // Lexer states encoded to/from integers
    //--------------------------------------------------------------------------
type LexState = int64

type ColorState =
    | Token = 1
    | IfDefSkip = 3
    | String = 4
    | Comment = 5
    | CommentString = 6
    | CommentVString = 7
    | CamlOnly = 8
    | VString = 9
    | TokenizedComment = 10
    | EndLineThenSkip = 11
    | EndLineThenToken = 12
    
    | InitialState = 0 
    

module internal LexerStateEncoding = 

    let computeNextLexState token (prevLexcont:LexerWhitespaceContinuation) = 
      match token with 
      | HASH_LINE s
      | HASH_LIGHT s
      | HASH_IF(_, _, s)
      | HASH_ELSE(_, _, s)
      | HASH_ENDIF(_, _, s)
      | INACTIVECODE s
      | WHITESPACE s  
      | COMMENT s 
      | LINE_COMMENT s 
      | STRING_TEXT s 
      | EOF s -> s
      | BYTEARRAY _ | STRING _ -> LexCont.Token(prevLexcont.LexerIfdefStack)
      | _ -> prevLexcont

    // Note that this will discard all lexcont state, including the ifdefStack.
    let revertToDefaultLexCont = LexCont.Token []

    let resize32 (i:int32) : LexState = int64 i

    let lexstateNumBits = 4
    let ncommentsNumBits = 2
    let startPosNumBits = pos.EncodingSize
    let hardwhiteNumBits = 1
    let ifdefstackCountNumBits = 4
    let ifdefstackNumBits = 16           // 0 means if, 1 means else
    let _ = assert (lexstateNumBits 
                    + ncommentsNumBits 
                    + startPosNumBits 
                    + hardwhiteNumBits 
                    + ifdefstackCountNumBits 
                    + ifdefstackNumBits <= 64)

    let lexstateStart         = 0
    let ncommentsStart        = lexstateNumBits
    let startPosStart         = lexstateNumBits+ncommentsNumBits
    let hardwhitePosStart     = lexstateNumBits+ncommentsNumBits+startPosNumBits
    let ifdefstackCountStart  = lexstateNumBits+ncommentsNumBits+startPosNumBits+hardwhiteNumBits
    let ifdefstackStart       = lexstateNumBits+ncommentsNumBits+startPosNumBits+hardwhiteNumBits+ifdefstackCountNumBits
    
    let lexstateMask          = Bits.mask64 lexstateStart lexstateNumBits
    let ncommentsMask         = Bits.mask64 ncommentsStart ncommentsNumBits
    let startPosMask          = Bits.mask64 startPosStart startPosNumBits
    let hardwhitePosMask      = Bits.mask64 hardwhitePosStart hardwhiteNumBits
    let ifdefstackCountMask   = Bits.mask64 ifdefstackCountStart ifdefstackCountNumBits
    let ifdefstackMask        = Bits.mask64 ifdefstackStart ifdefstackNumBits

    let bitOfBool b = if b then 1 else 0
    let boolOfBit n = (n = 1L)
        
    let encodeLexCont (colorState:ColorState) ncomments (b:pos) ifdefStack light = 
        let mutable ifdefStackCount = 0
        let mutable ifdefStackBits = 0
        for ifOrElse in ifdefStack do
            match ifOrElse with 
                | (IfDefIf,_) -> ()
                | (IfDefElse,_) -> 
                    ifdefStackBits <- (ifdefStackBits ||| (1 <<< ifdefStackCount))
            ifdefStackCount <- ifdefStackCount + 1

        let lexstate = int64 colorState
        ((lexstate <<< lexstateStart)  &&& lexstateMask)
        ||| ((ncomments <<< ncommentsStart) &&& ncommentsMask)
        ||| ((resize32 b.Encoding <<< startPosStart) &&& startPosMask)
        ||| ((resize32 (bitOfBool light) <<< hardwhitePosStart) &&& hardwhitePosMask)
        ||| ((resize32 ifdefStackCount <<< ifdefstackCountStart) &&& ifdefstackCountMask)
        ||| ((resize32 ifdefStackBits <<< ifdefstackStart) &&& ifdefstackMask)
    
    let decodeLexCont (state:LexState) = 
        let mutable ifDefs = []
        let ifdefStackCount = (int32) ((state &&& ifdefstackCountMask) >>> ifdefstackCountStart)
        if ifdefStackCount>0 then 
            let ifdefStack = (int32) ((state &&& ifdefstackMask) >>> ifdefstackStart)
            for i in 1..ifdefStackCount do
                let bit = ifdefStackCount-i
                let mask = 1 <<< bit
                let ifDef = (if ifdefStack &&& mask = 0 then IfDefIf else IfDefElse)
                ifDefs<-(ifDef,range0)::ifDefs
        enum<ColorState> (int32 ((state &&& lexstateMask)  >>> lexstateStart)),
        (int32) ((state &&& ncommentsMask) >>> ncommentsStart),
        pos.Decode (int32 ((state &&& startPosMask) >>> startPosStart)),
        ifDefs,
        boolOfBit ((state &&& hardwhitePosMask) >>> hardwhitePosStart)

    let encodeLexInt lightSyntaxStatus (lexcont:LexerWhitespaceContinuation) = 
        let tag,n1,p1,ifd = 
            match lexcont with 
            | LexCont.Token ifd                   -> ColorState.Token,0L,pos0,ifd
            | LexCont.IfDefSkip (ifd,n,m)        -> ColorState.IfDefSkip,resize32 n,m.Start,ifd
            | LexCont.EndLine(LexerEndlineContinuation.Skip(ifd,n,m)) -> ColorState.EndLineThenSkip,resize32 n,m.Start,ifd
            | LexCont.EndLine(LexerEndlineContinuation.Token(ifd))    -> ColorState.EndLineThenToken,0L,pos0,ifd
            | LexCont.String (ifd,m)              -> ColorState.String,0L,m.Start,ifd
            | LexCont.Comment (ifd,n,m)           -> ColorState.Comment,resize32 n,m.Start,ifd
            | LexCont.TokenizedComment (ifd,n,m) -> ColorState.TokenizedComment,resize32 n,m.Start,ifd
            | LexCont.CommentString (ifd,n,m)    -> ColorState.CommentString,resize32 n,m.Start,ifd
            | LexCont.CommentVerbatimString (ifd,n,m)   -> ColorState.CommentVString,resize32 n,m.Start,ifd
            | LexCont.MLOnly (ifd,m)            -> ColorState.CamlOnly,0L,m.Start,ifd
            | LexCont.VerbatimString (ifd,m)             -> ColorState.VString,0L,m.Start,ifd
        encodeLexCont tag n1 p1 ifd lightSyntaxStatus
        

    let decodeLexInt (state:LexState) = 
        let tag,n1,p1,ifd,lightSyntaxStatusInital = decodeLexCont state 
        let lexcont = 
            match tag with 
            |  ColorState.Token -> LexCont.Token ifd
            |  ColorState.IfDefSkip -> LexCont.IfDefSkip (ifd,n1,mkRange "file" p1 p1)
            |  ColorState.String -> LexCont.String (ifd,mkRange "file" p1 p1)
            |  ColorState.Comment -> LexCont.Comment (ifd,n1,mkRange "file" p1 p1)
            |  ColorState.TokenizedComment -> LexCont.TokenizedComment (ifd,n1,mkRange "file" p1 p1)
            |  ColorState.CommentString -> LexCont.CommentString (ifd,n1,mkRange "file" p1 p1)
            |  ColorState.CommentVString -> LexCont.CommentVerbatimString (ifd,n1,mkRange "file" p1 p1)
            |  ColorState.CamlOnly -> LexCont.MLOnly (ifd,mkRange "file" p1 p1)
            |  ColorState.VString -> LexCont.VerbatimString (ifd,mkRange "file" p1 p1)
            |  ColorState.EndLineThenSkip -> LexCont.EndLine(LexerEndlineContinuation.Skip(ifd,n1,mkRange "file" p1 p1))
            |  ColorState.EndLineThenToken -> LexCont.EndLine(LexerEndlineContinuation.Token(ifd))
            | _ -> LexCont.Token [] 
        lightSyntaxStatusInital,lexcont

    let callLexCont lexcont args skip lexbuf = 
        let argsWithIfDefs ifd = 
            if !args.ifdefStack = ifd then 
                args
            else 
                {args with ifdefStack = ref ifd}
        match lexcont with 
        | LexCont.EndLine(cont)              -> Lexer.endline cont args skip lexbuf
        | LexCont.Token ifd                  -> Lexer.token (argsWithIfDefs ifd) skip lexbuf 
        | LexCont.IfDefSkip (ifd,n,m)       -> Lexer.ifdef_skip n m (argsWithIfDefs ifd) skip lexbuf 
                                                                           //v-- What's this magic number for? 
                                                                           // answer: it's just an initial buffer size. 
        | LexCont.String (ifd,m)             -> Lexer.string (ByteBuffer.Create 100,defaultStringFinisher,m,(argsWithIfDefs ifd)) skip lexbuf
        | LexCont.Comment (ifd,n,m)          -> Lexer.comment(n,m,(argsWithIfDefs ifd)) skip lexbuf
        | LexCont.TokenizedComment (ifd,n,m)-> 
            // The first argument is 'None' because we don't need XML comments when called from VS
            Lexer.tokenized_comment(None,n,m,(argsWithIfDefs ifd)) skip lexbuf
        | LexCont.CommentString (ifd,n,m)   -> Lexer.comment_string n m (argsWithIfDefs ifd) skip lexbuf
        | LexCont.CommentVerbatimString (ifd,n,m)  -> Lexer.comment_vstring n m (argsWithIfDefs ifd) skip lexbuf
        | LexCont.MLOnly (ifd,m)           -> Lexer.camlonly m (argsWithIfDefs ifd) skip lexbuf
        | LexCont.VerbatimString (ifd,m)            -> Lexer.vstring (ByteBuffer.Create 100,defaultStringFinisher,m,(argsWithIfDefs ifd)) skip lexbuf

//----------------------------------------------------------------------------
// Colorization
//----------------------------------------------------------------------------

// Information beyond just tokens that can be derived by looking at just a single line.
// For example metacommands like #load.
type SingleLineTokenState =
    | BeforeHash = 0
    | NoFurtherMatchPossible = 1
    

/// Split a line into tokens and attach information about the tokens. This information is used by Visual Studio.
[<Sealed>]
type internal LineTokenizer(text:string, 
                            filename : string, 
                            lexArgsLightOn : lexargs,
                            lexArgsLightOff : lexargs
                            ) = 

    let skip = false   // don't skip whitespace in the lexer 
    let lexbuf = UnicodeLexing.StringAsLexbuf text
    
    let mutable singleLineTokenState = SingleLineTokenState.BeforeHash
    let fsx = Build.IsScript(filename)

    // ----------------------------------------------------------------------------------
    // This implements post-processing of #directive tokens - not very elegant, but it works...
    // We get the whole "   #if IDENT // .. .. " thing as a single token from the lexer,
    // so we need to split it into tokens that are used by VS for colorization
    
    // Stack for tokens that are split during postrpocessing    
    let mutable tokenStack = new Stack<_>()
    let delayToken tok = tokenStack.Push(tok)

    // Process: anywhite* #<directive>
    let processDirective (str:string) directiveLength delay cont =
        let hashIdx = str.IndexOf("#")
        if (hashIdx <> 0) then delay(WHITESPACE cont, 0, hashIdx - 1)
        delay(HASH_IF(range0, "", cont), hashIdx, hashIdx + directiveLength)
        hashIdx + directiveLength + 1
    
    // Process: anywhite* ("//" [^'\n''\r']*)?
    let processWhiteAndComment (str:string) offset delay cont = 
        let rest = str.Substring(offset, str.Length - offset)
        let comment = rest.IndexOf('/')
        let spaceLength = if comment = -1 then rest.Length else comment
        if (spaceLength > 0) then delay(WHITESPACE cont, offset, offset + spaceLength - 1)
        if (comment <> -1) then delay(COMMENT(cont), offset + comment, offset + rest.Length - 1) 
    
    // Split a directive line from lexer into tokens usable in VS
    let processDirectiveLine ofs f =
        let delayed = new ResizeArray<_>()
        f (fun (tok, s, e) -> delayed.Add (tok, s + ofs, e + ofs) )
        // delay all the tokens and return the remaining one
        for i = delayed.Count - 1 downto 1 do delayToken delayed.[i]
        delayed.[0]
      
    // Split the following line:
    //  anywhite* ("#else"|"#endif") anywhite* ("//" [^'\n''\r']*)?
    let processHashEndElse ofs (str:string) length cont = 
        processDirectiveLine ofs (fun delay ->
            // Process: anywhite* "#else"   /   anywhite* "#endif"
            let offset = processDirective str length delay cont
            // Process: anywhite* ("//" [^'\n''\r']*)?
            processWhiteAndComment str offset delay cont )            
          
    // Split the following line:
    //  anywhite* "#if" anywhite+ ident anywhite* ("//" [^'\n''\r']*)?
    let processHashIfLine ofs (str:string) cont =
        let With n m = if (n < 0) then m else n
        processDirectiveLine ofs (fun delay ->
            // Process: anywhite* "#if"
            let offset = processDirective str 2 delay cont      
            // Process: anywhite+ ident
            let rest, spaces = 
                (let w = str.Substring(offset) 
                 let r = w.TrimStart([| ' '; '\t' |]) 
                 r, w.Length - r.Length)      
            let beforeIdent = offset + spaces      
            let identLength = With (rest.IndexOfAny([| '/'; '\t'; ' ' |])) rest.Length 
            delay(WHITESPACE cont, offset, beforeIdent - 1)            
            delay(IDENT(rest.Substring(0, identLength)), beforeIdent, beforeIdent + identLength - 1)           
            // Process: anywhite* ("//" [^'\n''\r']*)?
            let offset = beforeIdent + identLength
            processWhiteAndComment str offset delay cont )
      
    // ----------------------------------------------------------------------------------
          


    do resetLexbufPos filename lexbuf 
    
    member t.StartNewLine() = 
        singleLineTokenState <- SingleLineTokenState.BeforeHash

    member x.ScanToken(lexintInitial) : Option<TokenInformation> * LexState = 
        use unwindBP = PushThreadBuildPhaseUntilUnwind (BuildPhase.Parse)
    
        // Ideally we would explicitly install DiscardErrorsLogger here. However different threads are using the error logger!
        // Indeed we really have to move to a model where the error logger is passed explicitly everywhere in the codebase.
        // For the moment we jsut make the default global error logger discard errors.
        use unwindEL = PushErrorLoggerPhaseUntilUnwind (fun _ -> DiscardErrorsLogger)
        let lightSyntaxStatusInital, lexcontInitial = LexerStateEncoding.decodeLexInt lexintInitial 
        let lightSyntaxStatus = LightSyntaxStatus(lightSyntaxStatusInital,false)  

        // Build the arguments to the lexer function
        let lexargs = if lightSyntaxStatusInital then lexArgsLightOn else lexArgsLightOff

        let GetTokenWithPosition(lexcontInitial) = 
            // Column of token
            let ColumnsOfCurrentToken() = 
                let leftp = lexbuf.StartPos 
                let rightp = lexbuf.EndPos 
                let leftc = leftp.Column 
                let rightc = if rightp.Line > leftp.Line then text.Length else rightp.Column 
                let rightc = rightc - 1   
                leftc,rightc

            // Get the token & position - either from a stack or from the lexer        
            try 
                if (tokenStack.Count > 0) then true, tokenStack.Pop()
                else                    
                  // Choose which lexer entrypoint to call and call it
                  let token = LexerStateEncoding.callLexCont lexcontInitial lexargs skip lexbuf 
                  let leftc, rightc = ColumnsOfCurrentToken()
                  
                  // Splits tokens like ">." into multiple tokens - this duplicates behavior from the 'lexfilter'
                  // which cannot be (easily) used from the langauge service. The rules here are not always valid,
                  // because sometimes token shouldn't be split. However it is just for colorization & 
                  // for VS (which needs to recognize when user types ".") - note, this doesn't show any intellisense
                  // if you have custom operator (e.g. ">.>", because there won't be any info available)
                  match token with
                  | HASH_IF(m, lineStr, cont) when lineStr <> "" ->
                      false, processHashIfLine m.StartColumn lineStr cont
                  | HASH_ELSE(m, lineStr, cont) when lineStr <> "" ->
                      false, processHashEndElse m.StartColumn lineStr 4 cont                  
                  | HASH_ENDIF(m, lineStr, cont) when lineStr <> "" ->
                      false, processHashEndElse m.StartColumn lineStr 5 cont
                  | RQUOTE_DOT (s,raw) -> 
                      delayToken(DOT, rightc, rightc)
                      false, (RQUOTE (s,raw), leftc, rightc - 1)
                  | INFIX_COMPARE_OP (Lexfilter.TyparsCloseOp(greaters,afterOp) as opstr) -> 
                      match afterOp with
                      | None -> ()
                      | Some tok -> delayToken(tok, leftc + greaters.Length, rightc)
                      for i = greaters.Length - 1 downto 1 do
                          delayToken(greaters.[i] false, leftc + i, rightc - opstr.Length + i + 1)
                      false, (greaters.[0] false, leftc, rightc - opstr.Length + 1)
                  | _ -> false, (token, leftc, rightc)
            with
            | e -> false, (EOF LexerStateEncoding.revertToDefaultLexCont, 0, 0) 
        
        // Grab a token
        let isCached, (token, leftc, rightc) = GetTokenWithPosition(lexcontInitial)
                 
        // Check for end-of-string and failure
        let tokenDataOption, lexcontFinal, tokenTag = 
            match token with 
            | EOF lexcont -> 
                // End of text! No more tokens.
                None,lexcont,0 
            | LEX_FAILURE s -> 
                Trace.PrintLine("Lexing", fun _ -> sprintf "LEX_FAILURE:%s\n" s)
                None, LexerStateEncoding.revertToDefaultLexCont, 0
            | _ ->
                // Get the information about the token
                let (colorClass,charClass,triggerClass) = TokenClassifications.tokenInfo token 
                let lexcontFinal = 
                    // If we're using token from cache, we don't move forward with lexing
                    if isCached then lexcontInitial else LexerStateEncoding.computeNextLexState token lexcontInitial 
                let tokenTag = tagOfToken token 
                let tokenData = {TokenName = token_to_string token; LeftColumn=leftc; RightColumn=rightc;ColorClass=colorClass;CharClass=charClass;TriggerClass=triggerClass;Tag=tokenTag} 
                Some(tokenData), lexcontFinal, tokenTag
                
        // Get the final lex int and color state                
        let FinalState(lexcontFinal) = 
            LexerStateEncoding.encodeLexInt lightSyntaxStatus.Status lexcontFinal 
                
        // Check for patterns like #-IDENT and see if they look like meta commands for .fsx files. If they do then merge them into a single token.
        let tokenDataOption,lexintFinal = 
            let lexintFinal = FinalState(lexcontFinal)
            match tokenDataOption, singleLineTokenState, tokenTagToTokenId tokenTag with 
            | Some(tokenData), SingleLineTokenState.BeforeHash, TOKEN_HASH ->
                // Don't allow further matches.
                singleLineTokenState <- SingleLineTokenState.NoFurtherMatchPossible
                // Peek at the next token
                let isCached, (nextToken, _, rightc) = GetTokenWithPosition(lexcontInitial)
                match nextToken with 
                | IDENT possibleMetacommand -> 
                    match fsx,possibleMetacommand with
                    // These are for script (.fsx and .fsscript) files.
                    | true,"r" 
                    | true,"reference" 
                    | true,"I" 
                    | true,"load" 
                    | true,"time" 
                    | true,"cd" 
#if DEBUG
                    | true,"terms" 
                    | true,"types" 
                    | true,"savedll" 
                    | true,"nosavedll" 
#endif
                    | true,"silentCd" 
                    | true,"q" 
                    | true,"quit" 
                    | true,"help" 
                    // These are for script and non-script
                    | _,"nowarn" -> 
                        // Merge both tokens into one.
                        let lexcontFinal = if (isCached) then lexcontInitial else LexerStateEncoding.computeNextLexState token lexcontInitial 
                        let tokenData = {tokenData with RightColumn=rightc;ColorClass=TokenColorKind.PreprocessorKeyword;CharClass=TokenCharKind.Keyword;TriggerClass=TriggerClass.None} 
                        let lexintFinal = FinalState(lexcontFinal)
                        Some(tokenData),lexintFinal
                    | _ -> tokenDataOption,lexintFinal
                | _ -> tokenDataOption,lexintFinal
            | _, SingleLineTokenState.BeforeHash, TOKEN_WHITESPACE -> 
                // Allow leading whitespace.
                tokenDataOption,lexintFinal
            | _ -> 
                singleLineTokenState <- SingleLineTokenState.NoFurtherMatchPossible
                tokenDataOption,lexintFinal
            
        tokenDataOption, lexintFinal

[<Sealed>]
type SourceTokenizer(defineConstants : string list, filename : string) =     
    let lexResourceManager = new Lexhelp.LexResourceManager() 

    // the tokenizer doesn't need the value of __SOURCE_DIRECTORY__, so any value is fine               
    let dummySourceDirectoryFun = fun()->"C:\\" 
    
    let lexArgsLightOn = mkLexargs(dummySourceDirectoryFun,filename,defineConstants,LightSyntaxStatus(true,false),lexResourceManager, ref [],DiscardErrorsLogger) 
    let lexArgsLightOff = mkLexargs(dummySourceDirectoryFun,filename,defineConstants,LightSyntaxStatus(false,false),lexResourceManager, ref [],DiscardErrorsLogger) 
    
    member this.CreateLineTokenizer(line: string) = 
        LineTokenizer(line, filename, lexArgsLightOn, lexArgsLightOff)


type Severity = Warning | Error

type ErrorInfo = {
    StartLine:int
    EndLine:int
    StartColumn:int
    EndColumn:int
    Severity:Severity
    Message:string 
    Subcategory:string } with 
    override e.ToString()=
        sprintf "(%d,%d)-(%d,%d) %s %s %s" 
            e.StartLine e.StartColumn e.EndLine e.EndColumn
            e.Subcategory
            (if e.Severity=Warning then "warning" else "error") 
            e.Message    
            
    /// Decompose a warning or error into parts: position, severity, message
    static member CreateFromExceptionAndAdjustEof(exn,warn,trim:bool,fallbackRange:range, (linesCount:int, lastLength:int)) = 
        let r = ErrorInfo.CreateFromException(exn,warn,trim,fallbackRange)
                
        // Adjust to make sure that errors reported at Eof are shown at the linesCount        
        let startline, schange = min (r.StartLine, false) (linesCount, true)
        let endline,   echange = min (r.EndLine, false)   (linesCount, true)
        
        if not (schange || echange) then r
        else
            let r = if schange then { r with StartLine = startline; StartColumn = lastLength } else r
            if echange then { r with EndLine = endline; EndColumn = 1 + lastLength } else r

    /// Decompose a warning or error into parts: position, severity, message
    static member CreateFromException(exn,warn,trim:bool,fallbackRange:range) = 
        let adjust (p:pos) = (p.Line - 1),p.Column 
        let m = match RangeOfError exn with Some m -> m | None -> fallbackRange 
        let (s1:int),(s2:int) = adjust m.Start
        let (s3:int),(s4:int) = adjust (if trim then m.Start else m.End);
        let msg = bufs (fun buf -> OutputPhasedError buf exn false)
        {StartLine=s1; StartColumn=s2; EndLine=s3; EndColumn=s4; Severity=(if warn then Warning else Error); Subcategory=exn.Subcategory(); Message=msg}
        
    
/// Use to reset error and warning handlers            
type ErrorScope()  = 
    let mutable errors = [] 
    static let mutable mostRecentError = None
    let unwindBP = PushThreadBuildPhaseUntilUnwind (BuildPhase.TypeCheck)    
    let unwindEL =        
        PushErrorLoggerPhaseUntilUnwind (fun _oldLogger -> 
            { new ErrorLogger with 
                member x.WarnSink(exn) = 
                      errors <- ErrorInfo.CreateFromException(exn,true,false,range.Zero):: errors
                member x.ErrorSink(exn) = 
                      let err = ErrorInfo.CreateFromException(exn,false,false,range.Zero)
                      errors <- err :: errors
                      mostRecentError <- Some(err)
                member x.ErrorCount = errors.Length })
        
    member x.Errors = errors |> List.filter (fun error -> error.Severity = Error)
    member x.Warnings = errors |> List.filter (fun error -> error.Severity = Warning)
    member x.ErrorsAndWarnings = errors
    member x.TryGetFirstErrorText() =
        match x.Errors with 
        | error :: _ -> Some(error.Message)
        | [] -> None
    
    interface IDisposable with
          member d.Dispose() = 
              unwindEL.Dispose() (* unwind pushes when ErrorScope disposes *)
              unwindBP.Dispose()

    static member MostRecentError = mostRecentError
    
    static member Protect<'a> (m:range) (f:unit->'a) (err:string->'a) : 'a = 
        use errorScope = new ErrorScope()
        let res = 
            try 
                Some(f())
            with e -> errorRecovery e m; None
        match res with 
        | Some(res) ->res
        | None -> 
            match errorScope.TryGetFirstErrorText() with 
            | Some text -> err text
            | None -> err ""

    static member ProtectWithDefault m f dflt = 
        ErrorScope.Protect m f (fun _ -> dflt)

    static member ProtectAndDiscard m f = 
        ErrorScope.Protect m f (fun _ -> ())


//----------------------------------------------------------------------------
// Display characteristics of typechecking items
//--------------------------------------------------------------------------

/// Interface that defines methods for comparing objects using partial equality relation
type IPartialEqualityComparer<'T> = 
    inherit IEqualityComparer<'T>
    /// Can the specified object be tested for equality?
    abstract InEqualityRelation : 'T -> bool

type iDeclarationSet = int

/// Describe a comment as either a block of text or a file+signature reference into an intellidoc file.
type internal XmlComment =
    | XmlCommentNone
    | XmlCommentText of string
    | XmlCommentSignature of (*File and Signature*) string * string

/// A single data tip display element
type internal DataTipElement = 
    | DataTipElementNone
    /// A single type, method, etc with comment.
    | DataTipElement of (* text *) string * XmlComment
    /// For example, a method overload group.
    | DataTipElementGroup of ((* text *) string * XmlComment) list
    /// An error occurred formatting this element
    | DataTipElementCompositionError of string

/// Information for building a data tip box.
type internal DataTipText = 
    /// A list of data tip elements to display.
    | DataTipText of DataTipElement list  

/// Test hooks for tweaking internals
module internal TestHooks = 
    /// Function used to construct memebr info text in data tips.
    let FormatOverloadsToList: (DataTipElement->DataTipElement) option ref = ref None

    let FormatOverloadsToListScope(hook:(DataTipElement->DataTipElement)) : System.IDisposable = 
        FormatOverloadsToList := Some(hook)
        {new IDisposable with
            member d.Dispose() = 
                FormatOverloadsToList := None}     

    /// Enable/disable generation of interface file in the 'Find Declaration'
    /// By default, the feature is turned off in the current version
    let enableFsiGenerationHook = ref false  
    
    /// Turns the generation on and returns IDisposable that disables it again
    let EnableFsiGenerationScope () = 
        let oldFsiGenerationHook = !enableFsiGenerationHook
        enableFsiGenerationHook := true
        { new IDisposable with
              member x.Dispose() =
                  enableFsiGenerationHook := oldFsiGenerationHook }
        
module internal ItemDescriptions = 

    // Hardwired constants from older versions of Visual Studio. These constants were used with Babel and VS internals.
    let iIconGroupClass = 0x0000
    let iIconGroupConstant = 0x0001
    let iIconGroupDelegate = 0x0002 
    let iIconGroupEnum = 0x0003
    let iIconGroupEnumMember = 0x0004
    let iIconGroupEvent = 0x0005
    let iIconGroupException = 0x0006
    let iIconGroupFieldBlue = 0x0007
    let iIconGroupInterface = 0x0008 // Absolute = 48
    let iIconGroupTextLine = 0x0009
    let iIconGroupScript = 0x000a
    let iIconGroupScript2 = 0x000b
    let iIconGroupMethod = 0x000c
    let iIconGroupMethod2 = 0x000d
    let iIconGroupModule = 0x000e
    let iIconGroupNameSpace = 0x000f // Absolute = 90
    let iIconGroupFormula = 0x0010
    let iIconGroupProperty = 0x00011
    let iIconGroupStruct = 0x00012
    let iIconGroupTemplate = 0x00013
    let iIconGroupTypedef = 0x00014
    let iIconGroupType = 0x00015
    let iIconGroupUnion = 0x00016
    let iIconGroupVariable = 0x00017
    let iIconGroupValueType = 0x00018 // Absolute = 144
    let iIconGroupIntrinsic = 0x00019
    let iIconGroupError = 0x0001f
    let iIconGroupFieldYellow = 0x0020
    let iIconGroupMisc1 = 0x00021
    let iIconGroupMisc2 = 0x0022
    let iIconGroupMisc3 = 0x00023

    let iIconItemPublic = 0x0000
    let iIconItemInternal = 0x0001
    let iIconItemSpecial = 0x0002
    let iIconItemProtected = 0x0003
    let iIconItemPrivate = 0x0004
    let iIconItemShortCut = 0x0005
    let iIconItemNormal  = iIconItemPublic

    let iIconBlackBox = 162
    let iIconLibrary = 163
    let iIconProgram = 164
    let iIconWebProgram = 165
    let iIconProgramEmpty = 166
    let iIconWebProgramEmpty = 167

    let iIconComponents = 168
    let iIconEnvironment = 169
    let iIconWindow = 170
    let iIconFolderOpen = 171
    let iIconFolder = 172
    let iIconArrowRight = 173

    let iIconAmbigious = 174
    let iIconShadowClass = 175
    let iIconShadowMethodPrivate = 176
    let iIconShadowMethodProtected = 177
    let iIconShadowMethod = 178
    let iIconInCompleteSource = 179

    let isFunction g typ =
      let _,tau = tryDestForallTy g typ
      isFunTy g tau 

     
    let OutputFullTypeName ppF fnF os r = 
        match ppF r with 
        | None -> ()
        | Some _ -> 
            bprintf os "\n\n%s: %s" (FSComp.SR.typeInfoFullName()) (fnF r)
          
    // Format the supertypes and other useful information about a type to a buffer
    let OutputUsefulTypeInfo (infoReader:InfoReader) m denv os ty = 
        let g = infoReader.g
        let amap = infoReader.amap
        let GetSuperTypes() =
            let supertypes = AllSuperTypesOfType g amap m AllowMultiIntfInst ty
            let supertypes = supertypes |> List.filter (AccessibilityLogic.IsTypeAccessible g AccessibleFromSomewhere) 
            let supertypes = supertypes |> List.filter (typeEquiv g g.obj_ty >> not) 
            let selfs,supertypes = supertypes |> List.partition (typeEquiv g ty) 
            let supertypesC,supertypesI = supertypes |> List.partition (isInterfaceTy g)
            let supertypes = selfs @ supertypesC @ supertypesI
            supertypes

        let ConstructUsefulTypeInfo() =  
            let supertypes = GetSuperTypes()
            let supertypeLs,_ = NicePrint.typesAndConstraintsL denv supertypes 
            let isEnum = isEnumTy g ty
            if List.length supertypes > 1 then 
                bprintf os "\n\n";
                List.zip supertypes supertypeLs |> List.iter (fun (superty,supertyL) -> 
                    if typeEquiv g superty ty then bprintf os "  %s: %a\n" (FSComp.SR.typeInfoType()) bufferL supertyL
                    elif isClassTy g superty || isInterfaceTy g ty then bprintf os "  %s: %a\n" (FSComp.SR.typeInfoInherits()) bufferL supertyL
                    elif not(isEnum) then bprintf os "  %s: %a\n" (FSComp.SR.typeInfoImplements()) bufferL supertyL)
        ErrorScope.ProtectAndDiscard m (fun () -> ConstructUsefulTypeInfo())
           

    let rangeOfPropInfo (pinfo:PropInfo) = pinfo.ArbitraryValRef |> Option.map (fun v -> v.Range)
    let rangeOfMethInfo (minfo:MethInfo) = minfo.ArbitraryValRef |> Option.map (fun v -> v.Range)
      
    let rangeOfItem g isDecl d = 
        // skip all default generated constructors for structs
        let (|FilterDefaultStructCtors|) ctors =
            ctors |> List.filter (function DefaultStructCtor _ -> false | _ -> true)

        match d with
        | Item.Value vref -> Some(if isDecl then vref.Range else vref.DefinitionRange)
        | Item.UnionCase(ucr) -> Some(ucr.UnionCase.Range)
        | Item.ActivePatternCase(apref) -> Some(apref.ActivePatternVal.Range)
        | Item.ExnCase(ecr) -> Some(ecr.Range)
        | Item.RecdField(rfinfo) -> Some(rfinfo.RecdFieldRef.Range)
        | Item.Event _ | Item.ILField _ -> None
        | Item.Property(_,pinfos) -> (List.head pinfos) |> rangeOfPropInfo 
        | Item.MethodGroup(_,(minfo :: _)) 
        | Item.CtorGroup(_,FilterDefaultStructCtors(minfo :: _)) -> rangeOfMethInfo minfo
        | Item.Types(_,(typ :: _)) -> Some((tcrefOfAppTy g typ).Range)
        | Item.ModuleOrNamespaces(modref :: _) -> Some(modref.Range)
        | Item.ActivePatternResult(APInfo(_, _),_, _, m) -> Some(m)
        | _ -> None

    let ccuOfItem g d = 
        match d with
        | Item.Value vref -> ccuOfValRef vref 
        | Item.UnionCase(ucr) -> ccuOfTyconRef ucr.TyconRef
        | Item.ActivePatternCase(apref) -> ccuOfValRef apref.ActivePatternVal
        | Item.ExnCase(ecr) -> ccuOfTyconRef ecr
        | Item.RecdField(rfinfo) -> ccuOfTyconRef rfinfo.RecdFieldRef.TyconRef
        | Item.Event _ | Item.ILField _ -> None
        | Item.Property(_,pinfos) -> (List.head pinfos).ArbitraryValRef |> Option.bind ccuOfValRef
        | Item.MethodGroup(_,(minfo :: _)) 
        | Item.CtorGroup(_,(minfo :: _)) -> minfo.ArbitraryValRef |> Option.bind ccuOfValRef
        | Item.Types(_,(typ :: _)) -> ccuOfTyconRef (tcrefOfAppTy g typ)
        | Item.ModuleOrNamespaces(modref :: _) -> ccuOfTyconRef modref
        | _ -> None

    /// Work out the likely source file for an item
    let fileNameOfItem g qualProjectDir (m:range) h =
        let file = m.FileName 
        dprintf "file stored in metadata is '%s'\n" file
        if not (Path.IsPathRooted file) then 
            match (ccuOfItem g h) with 
            | Some ccu -> 
                
                // Note: For F# library DLLs, the code in build.ml fixes uo the SourceCodeDirectory (compileTimeWorkingDir)
                // to be defaultFSharpBinariesDir\..\lib\<library-name>, i.e. the location of the source for the 
                // file in the F# installation location
                
                Path.Combine(ccu.SourceCodeDirectory, file)
            | None -> 
                match qualProjectDir with 
                | None     -> file
                | Some dir -> Path.Combine(dir, file)
         else file

    /// Cut long filenames to make them visually appealing 
    let cutFileName s = if String.length s > 40 then String.sub s 0 10 + "..."+String.sub s (String.length s - 27) 27 else s

    let libFileOfEntityRef x =
        match x with
        | ERefLocal _ -> None
        | ERefNonLocal nlref -> nlref.Ccu.FileName      

    // Find the name of the metadata file for this external definition 
    let metaInfoOfEntityRef (infoReader:InfoReader) m tcref = 
        let g = infoReader.g
        match tcref with 
        | ERefLocal _ -> None
        | ERefNonLocal nlref -> 
        match nlref.Ccu.FileName with
        | None -> None
        | Some libfile -> 
        (* Generalize the pinfo to get a formal signature *)
        let ftctps = tcref.Typars(m)
        let ftinst = generalizeTypars ftctps
        let ftinfo = ILTypeInfo.FromType g (TType_app(tcref,ftinst))
        Some(libfile,ftctps,ftinfo)

    /// This function gets the signature to pass to Visual Studio to use its lookup functions for .NET stuff. 
    let GetXmlDocHelpSigOfItemForLookup (infoReader:InfoReader) m d : XmlComment = 
        let result = 
            let amap = infoReader.amap
            let g = infoReader.g
                
            match d with
            | Item.ActivePatternCase (APElemRef(_, vref, _))        
            | Item.Value vref -> 
                if not (vref.IsLocalRef) then
                    match vref.nlr.Ccu.FileName with
                    | Some (libfile) -> 
                        let v = vref.Deref
                        if v.XmlDocSig = "" then
                            v.XmlDocSig <- XmlDocSigOfVal g vref.TopValActualParent.CompiledRepresentationForTyrepNamed.Name v
                        Some (libfile, v.XmlDocSig)
                    | None -> None
                else 
                    None
            | Item.UnionCase(uci) ->
                let tcref = uci.TyconRef
                if tcref.IsILTycon then
                    None
                else
                    match (libFileOfEntityRef tcref) with
                    | None -> None
                    | Some(libfile) -> 
                        if uci.UnionCase.XmlDocSig = "" then
                            uci.UnionCase.XmlDocSig <- XmlDocSigOfUnionCase "" uci.Name (tcref.CompiledRepresentationForTyrepNamed.FullName)
                        Some (libfile, uci.UnionCase.XmlDocSig)
            | Item.ExnCase(ecr) ->
                match (libFileOfEntityRef ecr) with
                | None -> None
                | Some(libfile) -> 
                    let e = ecr.Deref
                    if e.XmlDocSig = "" then
                        e.XmlDocSig <- XmlDocSigOfEntity ecr
                    Some (libfile, e.XmlDocSig)            
            | Item.RecdField(rfinfo) ->
                let tcref = rfinfo.TyconRef
                match (libFileOfEntityRef tcref) with
                | None -> None
                | Some(libfile) -> 
                    if rfinfo.RecdField.XmlDocSig = "" then
                        rfinfo.RecdField.XmlDocSig <- XmlDocSigOfField "" rfinfo.Name (tcref.CompiledRepresentationForTyrepNamed.FullName)
                    Some (libfile, rfinfo.RecdField.XmlDocSig)            
            | Item.NewDef _ -> None
            | Item.ILField(ILFieldInfo(tinfo, fdef)) -> 
                 let tcref = tinfo.TyconRef 
                 metaInfoOfEntityRef infoReader m tcref 
                 |> Option.map (fun (libfile,_,ftinfo) -> 
                     (libfile,
                      "F:"+ftinfo.ILTypeRef.FullName+"."+fdef.Name))
            | Item.Types(_,((TType_app(tcref,_)) :: _)) -> 
                 // Find the name of the metadata file for this external definition 
                 if tcref.IsILTycon then 
                   metaInfoOfEntityRef infoReader m tcref 
                   |> Option.map (fun (libfile,_,ftinfo) -> 
                     (libfile,"T:"+ftinfo.ILTypeRef.FullName))
                 else
                    match (libFileOfEntityRef tcref) with
                    | None -> None
                    | Some(libfile) ->
                        let tc = tcref.Deref 
                        if tc.XmlDocSig = "" then
                            tc.XmlDocSig <- XmlDocSigOfEntity tcref
                        Some (libfile, tc.XmlDocSig)
            | Item.ModuleOrNamespaces(modref :: _) ->
                match (libFileOfEntityRef modref) with
                | None -> None
                | Some(libfile) -> 
                    let m = modref.Deref
                    if m.XmlDocSig = "" then
                        m.XmlDocSig <- XmlDocSigOfEntity modref
                    Some (libfile, m.XmlDocSig)
            | Item.Property(_,(pinfo :: _)) -> 
                match pinfo with 
                | FSProp (g,typ,_,_) as fspinfo -> 
                    let tcref = tcrefOfAppTy g typ
                    let vref =
                        match fspinfo with 
                        | FSProp (_,_,Some(vref),_)
                        | FSProp (_,_,_,Some(vref)) -> vref
                        | _ -> failwith "unreachable"
                    match (libFileOfEntityRef tcref) with
                    | None -> None
                    | Some(libfile) -> 
                        let v = vref.Deref
                        if v.XmlDocSig = "" then
                            v.XmlDocSig <- XmlDocSigOfVal g (buildAccessPath vref.TopValActualParent.CompilationPathOpt) v
                        Some (libfile, v.XmlDocSig)                
                | ILProp(g, (ILPropInfo(tinfo,pdef))) -> 
                   let tcref = tinfo.TyconRef
                   metaInfoOfEntityRef infoReader m tcref 
                   |> Option.map (fun (libfile,ftctps,ftinfo) -> 
                       let filpinfo = ILPropInfo(ftinfo,pdef)
                       (libfile,
                        "P:"+ftinfo.ILTypeRef.FullName+"."+pdef.Name+
                        XmlDocArgsEnc g (ftctps,[]) (List.map snd (filpinfo.ParamNamesAndTypes(amap,m)))))
            | Item.Event(ILEvent(_,ilEventInfo) as einfo) ->
                let tinfo = ilEventInfo.ILTypeInfo 
                let tcref = tinfo.TyconRef 
                metaInfoOfEntityRef infoReader m tcref 
                |> Option.map (fun (libfile,_,ftinfo) -> 
                    (libfile,
                     "E:"+ftinfo.ILTypeRef.FullName+"."+einfo.EventName))
            | Item.MethodGroup(_,minfos) 
            | Item.CtorGroup(_,minfos) -> 
                match minfos with 
                | [] -> None
                | (FSMeth (g,typ,vref,_)) :: _ ->
                    let tcref = tcrefOfAppTy g typ
                    match (libFileOfEntityRef tcref) with
                    | None -> None
                    | Some(libfile) -> 
                        let v = vref.Deref
                        if v.XmlDocSig = "" then
                            v.XmlDocSig <- XmlDocSigOfVal g (buildAccessPath vref.TopValActualParent.CompilationPathOpt) v
                        Some (libfile, v.XmlDocSig)                
                | (ILMeth (g,ILMethInfo(tinfo,isExt,mdef,fmtps),_) as minfo) :: _ -> 
                   let actualTypeName = tinfo.ILTypeRef.FullName
                   let tcref = tinfo.TyconRef 
                   let genArity = if fmtps.Length=0 then "" else sprintf "``%d" fmtps.Length

                   metaInfoOfEntityRef infoReader m tcref 
                   |> Option.map (fun (libfile,ftctps,ftinfo) -> 
                       let filminfo = ILMethInfo(ftinfo,isExt,mdef,fmtps) 
                       (libfile,
                        "M:"+actualTypeName+"."+mdef.Name+genArity+XmlDocArgsEnc g (ftctps,fmtps) (filminfo.ArgTypes(amap,m,minfo.FormalMethodInst))))
                | (DefaultStructCtor _ :: _) -> None
            |  _ -> None
        match result with
        | Some(file,index) -> XmlCommentSignature(file,index)
        | None -> XmlCommentNone

    /// Produce an XmlComment with a signature or raw text.
    let GetXmlComment (xmlDoc:XmlDoc) (infoReader:InfoReader) m d : XmlComment = 
        let sb = StringBuilder()
        match xmlDoc with 
        | XmlDoc([| |]) -> ()
        | XmlDoc(l) -> 
            bprintf sb "\n"; 
            l |> Array.iter (fun (s:string) -> 
                // Note: this code runs for local/within-project xmldoc tooltips, but not for cross-project or .XML
                bprintf sb "\n%s" s) 

        let result = sb.ToString()
        let xml = 
            if String.IsNullOrEmpty(result) then XmlCommentNone
            else XmlCommentText(result)

        match xml with
        | XmlCommentNone -> GetXmlDocHelpSigOfItemForLookup infoReader m d
        | xmlComment -> xmlComment 

    /// Output a method info
    let FormatOverloadsToList (infoReader:InfoReader) m denv d minfos : DataTipElement = 
        let Format(minfo) = 
            let os = StringBuilder()
            FormatMethInfoToBuffer  infoReader.amap m denv os minfo

            let xml = GetXmlComment (if (minfo.IsInThisAssembly infoReader.g.compilingFslib) then minfo.XmlDoc else XmlDoc [||]) infoReader m d 
            let text : string = os.ToString()
            text,xml

        let result = DataTipElementGroup(minfos |> List.map Format)

        match !TestHooks.FormatOverloadsToList with 
        | Some(hook) -> hook(result)
        | None -> result

        
    let pubpath_of_vref         (v:ValRef) = v.PublicPath        
    let pubpath_of_tcref        (x:TyconRef) = x.PublicPath


    // Wrapper type for use by the 'partialDistinctBy' function
    [<StructuralEquality; NoComparison>]
    type WrapType<'T> = Wrap of 'T
    
    // Like Seq.distinctBy but only filters out duplicates for some of the elements
    let partialDistinctBy (per:IPartialEqualityComparer<_>) seq =
        // Wrap a Wrap(_) aroud all keys in case the key type is itself a type using null as a representation
        let dict = new Dictionary<WrapType<'T>,obj>(per)
        seq |> List.filter (fun v -> 
            let v = Wrap(v)
            if (per.InEqualityRelation(v)) then 
                if dict.ContainsKey(v) then false else (dict.[v] <- null; true)
            else true)


    /// Specifies functions for comparing 'Item' objects with respect to the user 
    /// (this means that some values that are not technically equal are treated as equal 
    ///  if this is what we want to show to the user, because we're comparing just the name
    //   for some cases e.g. when using 'fullDisplayTextOfModRef')
    let ItemDisplayPartialEquality g = 
      { new IPartialEqualityComparer<_> with   
          member x.InEqualityRelation item = 
              match item  with 
              | Wrap(Item.Types(_,[_])) -> true
              | Wrap(Item.ILField(ILFieldInfo(_, _))) -> true
              | Wrap(Item.RecdField(_)) -> true
              | Wrap(Item.PropName(_)) -> true
              | Wrap(Item.ModuleOrNamespaces(_ :: _)) -> true
              | Wrap(Item.MethodGroup(_, _)) -> true
              | Wrap(Item.Value(_)) -> true
              | Wrap(Item.ActivePatternCase _) -> true
              | Wrap(Item.DelegateCtor(_)) -> true
              | Wrap(Item.UnionCase(_)) -> true
              | Wrap(Item.ExnCase(_)) -> true              
              | Wrap(Item.Event(_)) -> true
              | Wrap(Item.Property(_)) -> true
              | _ -> false
              
          member x.Equals(item1, item2) = 
            // This may explore assemblies that are not in the reference set.
            // In this case just bail out and assume items are not equal
            protectAssemblyExploration false (fun () -> 
              let equalTypes(ty1, ty2) =
                  if isAppTy g ty1 && isAppTy g ty2 then tyconRefEq g (tcrefOfAppTy g ty1) (tcrefOfAppTy g ty2) 
                  else typeEquiv g ty1 ty2
              match item1,item2 with 
              | Wrap(Item.DelegateCtor(ty1)), Wrap(Item.DelegateCtor(ty2)) -> equalTypes(ty1, ty2)
              | Wrap(Item.Types(dn1,[ty1])), Wrap(Item.Types(dn2,[ty2])) -> 
                  // Bug 4403: We need to compare names as well, because 'int' and 'Int32' are physically the same type, but we want to show both
                  dn1 = dn2 && equalTypes(ty1, ty2) 
              | Wrap(Item.ExnCase(tcref1)), Wrap(Item.ExnCase(tcref2)) -> tyconRefEq g tcref1 tcref2
              | Wrap(Item.ILField(ILFieldInfo(_, fld1))), Wrap(Item.ILField(ILFieldInfo(_, fld2))) -> 
                  fld1 === fld2 // reference equality on the object identity of the AbstractIL metadata blobs for the fields
              | Wrap(Item.ModuleOrNamespaces(modref1 :: _)), Wrap(Item.ModuleOrNamespaces(modref2 :: _)) -> fullDisplayTextOfModRef modref1 = fullDisplayTextOfModRef modref2
              | Wrap(Item.PropName(id1)), Wrap(Item.PropName(id2)) -> (id1.idRange, id1.idText) = (id2.idRange, id2.idText)
              | Wrap(Item.MethodGroup(_, meths1)), Wrap(Item.MethodGroup(_, meths2)) -> 
                  Seq.zip meths1 meths2 |> Seq.forall (fun (m1, m2) ->
                    Infos.MethInfosUseIdenticalDefinitions () m1 m2)
              | Wrap(Item.Value(vref1)), Wrap(Item.Value(vref2)) -> valRefEq g vref1 vref2
              | Wrap(Item.ActivePatternCase(APElemRef(_apinfo1, vref1, idx1))), Wrap(Item.ActivePatternCase(APElemRef(_apinfo2, vref2, idx2))) ->
                  idx1 = idx2 && valRefEq g vref1 vref2
              | Wrap(Item.UnionCase(UnionCaseInfo(_, ur1))), Wrap(Item.UnionCase(UnionCaseInfo(_, ur2))) -> g.ucref_eq ur1 ur2
              | Wrap(Item.RecdField(RecdFieldInfo(_, RFRef(tcref1, n1)))), Wrap(Item.RecdField(RecdFieldInfo(_, RFRef(tcref2, n2)))) -> 
                  (tyconRefEq g tcref1 tcref2) && (n1 = n2) // there is no direct function as in the previous case
              | Wrap(Item.Property(_, pi1s)), Wrap(Item.Property(_, pi2s)) -> 
                  List.zip pi1s pi2s |> List.forall(fun (pi1, pi2) -> Infos.PropInfosUseIdenticalDefinitions pi1 pi2)
              | Wrap(Item.Event(evt1)), Wrap(Item.Event(evt2)) -> Infos.EventInfosUseIdenticalDefintions evt1 evt2
              | _ -> false)
              
          member x.GetHashCode item =
            // This may explore assemblies that are not in the reference set.
            // In this case just bail out and use a random hash code
            protectAssemblyExploration 1027 (fun () -> 
              match item with 
              | Wrap(Item.DelegateCtor(ty)) 
              | Wrap(Item.Types(_,[ty]))  -> 
                  if isAppTy g ty then hash (tcrefOfAppTy g ty).Stamp
                  else 1010
              | Wrap(Item.ILField(ILFieldInfo(_, fld))) -> 
                  System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode fld // hash on the object identity of the AbstractIL metadata blob for the field
              | Wrap(Item.ModuleOrNamespaces(modref :: _)) -> hash (fullDisplayTextOfModRef modref)          
              | Wrap(Item.PropName(id)) -> hash (id.idRange, id.idText)
              | Wrap(Item.MethodGroup(_, meths)) -> meths |> List.fold (fun st a -> st + (Infos.GetMethInfoHashCode(a))) 0
              | Wrap(Item.Value(vref)) -> hash vref.LogicalName
              | Wrap(Item.ActivePatternCase(APElemRef(_apinfo, vref, idx))) -> hash (vref.LogicalName, idx)
              | Wrap(Item.ExnCase(tcref)) -> hash tcref.Stamp
              | Wrap(Item.UnionCase(UnionCaseInfo(_, UCRef(tcref, n)))) -> hash(tcref.Stamp, n)
              | Wrap(Item.RecdField(RecdFieldInfo(_, RFRef(tcref, n)))) -> hash(tcref.Stamp, n)
              | Wrap(Item.Event(evt)) -> Infos.GetEventInfoHashCode(evt)
              | Wrap(Item.Property(_name, pis)) -> hash (pis |> List.map Infos.GetPropInfoHashCode)
              | _ -> failwith "unreachable") }
    
    // Remove items containing the same module references, where f projects out the module reference
    let RemoveDuplicateModuleRefs modrefs  = 
        modrefs |> partialDistinctBy 
                      { new IPartialEqualityComparer<WrapType<ModuleOrNamespaceRef>> with
                          member x.InEqualityRelation _ = true
                          member x.Equals(Wrap(item1), Wrap(item2)) = (fullDisplayTextOfModRef item1 = fullDisplayTextOfModRef item2)
                          member x.GetHashCode(Wrap(item)) = hash item.Stamp  }

    /// Remove all duplicate items
    let RemoveDuplicateItems g items = 
        items |> partialDistinctBy (ItemDisplayPartialEquality g) 

    /// Filter types that are explicitly suppressed from the IntelliSense (such as uppercase "FSharpList", "Option", etc.)
    let RemoveExplicitlySuppressed g items = 
      items |> List.filter (fun item ->
        // This may explore assemblies that are not in the reference set.
        // In this case just assume the item is not suppressed.
        protectAssemblyExploration true (fun () -> 
         match item with 
         | Item.Types(it, [ty]) -> g.suppressed_types |> List.forall (fun supp -> 
            if isAppTy g ty then 
              // check if they are the same logical type (after removing all abbreviations)
              let tcr1 = tcrefOfAppTy g ty
              let tcr2 = tcrefOfAppTy g (generalizedTyconRef supp) 
              not(tyconRefEq g tcr1 tcr2 && 
                  // check the display name is precisely the one we're suppressing
                  it = supp.DisplayName)
            else true ) 
         | _ -> true ))
    
    let SimplerDisplayEnv denv = { denv with suppressInlineKeyword=true; shortConstraints=true; showConstraintTyparAnnotations=false; abbreviateAdditionalConstraints=false }

    /// Output a the description of a language item
    let FormatItemDescriptionToDataTipElement (infoReader:InfoReader) m denv d : DataTipElement = 
        let g = infoReader.g
        let amap = infoReader.amap
        let denv = SimplerDisplayEnv denv
        match d with
        | Item.Value v ->            
            let os = StringBuilder()
            bprintf os "%a%a" 
                (NicePrint.outputQualifiedValSpec denv) v.Deref 
                (OutputFullTypeName pubpath_of_vref fullDisplayTextOfValRef) v;

            // adjust the type in case this is the 'this' pointer stored in a reference cell
            let ty = StripSelfRefCell(g, v.BaseOrThisInfo, v.Type) 

            OutputUsefulTypeInfo infoReader m denv os ty;

            let xml = GetXmlComment (if (valRefInThisAssembly g.compilingFslib v) then v.XmlDoc else XmlDoc [||]) infoReader m d 
            DataTipElement(os.ToString(), xml)
        // Union tags (constructors)
        | Item.UnionCase(ucr) -> 
            let os = StringBuilder()
            let uc = ucr.UnionCase 
            let rty = generalizedTyconRef ucr.TyconRef
            let recd = uc.RecdFields 
            let ty = if isNil recd then rty else (mkTupledTy g (recd |> List.map (fun rfld -> rfld.FormalType))) --> rty 
            bprintf os "%s %a.%s: %a"  (FSComp.SR.typeInfoUnionCase())
                (NicePrint.outputTyconRef denv) ucr.TyconRef
                (DecompileOpName uc.Id.idText) 
                (NicePrint.outputTy denv) ty;

            let xml = GetXmlComment (if (tyconRefUsesLocalXmlDoc g.compilingFslib ucr.TyconRef) then uc.XmlDoc else XmlDoc [||]) infoReader m d 
            DataTipElement(os.ToString(), xml)
        // Active pattern tag inside the declaration (result)             
        | Item.ActivePatternResult(APInfo(_, items), ty, idx, _) ->
            let os = StringBuilder()
            bprintf os "%s %s: %a" (FSComp.SR.typeInfoActivePatternResult()) (List.nth items idx) (NicePrint.outputTy denv) ty
            DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)
        // Active pattern tags 
        // XmlDoc is never emitted to xml doc files for these
        | Item.ActivePatternCase(apref) -> 
            let os = StringBuilder()
            let v = apref.ActivePatternVal
            
            // Format the type parameters to get e.g. ('a -> 'a) rather than ('?1234 -> '?1234)
            let _,tau = v.TypeScheme
            let _, ptau, _cxs = PrettyTypes.PrettifyTypes1 denv.g tau

            bprintf os "%s %s: %a%a" (FSComp.SR.typeInfoActiveRecognizer())
                apref.Name
                (NicePrint.outputTy denv) ptau 
                (OutputFullTypeName pubpath_of_vref fullDisplayTextOfValRef) v

            let xml = GetXmlComment v.XmlDoc infoReader m d 
            DataTipElement(os.ToString(), xml)
        // F# exception names
        | Item.ExnCase(ecref: TyconRef) -> 
            let os = StringBuilder()
            bprintf os "%a%a" (NicePrint.outputExnDef denv) ecref.Deref
              (OutputFullTypeName pubpath_of_tcref fullDisplayTextOfExnRef) ecref;
            
            let xml = GetXmlComment (if (tyconRefUsesLocalXmlDoc g.compilingFslib ecref) then ecref.XmlDoc else XmlDoc [||]) infoReader m d 
            DataTipElement(os.ToString(), xml)
        // F# record field names
        | Item.RecdField(rfinfo) ->
            let os = StringBuilder()
            let f = rfinfo.RecdField
            let _, ty, _cxs = PrettyTypes.PrettifyTypes1 g rfinfo.FieldType
            bprintf os "%a.%s: %a"  
                (NicePrint.outputTyconRef denv) rfinfo.TyconRef
                (DecompileOpName f.Name) 
                (NicePrint.outputTy denv) ty;
            match rfinfo.LiteralValue with 
            | None -> ()
            | Some lit -> 
               try bprintf os " = %s" (Layout.showL ( NicePrint.constL lit )) with _ -> ()

            let xml = GetXmlComment (if (tyconRefUsesLocalXmlDoc g.compilingFslib rfinfo.TyconRef) then f.rfield_xmldoc else XmlDoc [||]) infoReader m d 
            DataTipElement(os.ToString(), xml)
        // Not used
        | Item.NewDef(id) -> 
            let os = StringBuilder()
            bprintf os "%s %s" (FSComp.SR.typeInfoPatternVariable())id.idText
            DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)

        // .NET fields
        | Item.ILField(finfo) ->
            let os = StringBuilder()
            bprintf os "%s %a.%s" (FSComp.SR.typeInfoField()) (NicePrint.outputILTypeRef denv) finfo.ILTypeRef finfo.FieldName;
            match finfo.LiteralValue with 
            | None -> ()
            | Some v -> 
               try bprintf os " = %s" (Layout.showL ( NicePrint.constL (TypeChecker.TcFieldInit m v) )) 
               with _ -> ()
            DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)

        // .NET events
        | Item.Event(ILEvent(g,ilEventInfo) as einfo) ->
            let os = StringBuilder()
            let rty = PropTypOfEventInfo infoReader m AccessibleFromSomewhere einfo
            let _,rty, _cxs = PrettyTypes.PrettifyTypes1 g rty
            bprintf os "%s %a.%s: %a" (FSComp.SR.typeInfoEvent()) (NicePrint.outputILTypeRef denv) ilEventInfo.TypeRef einfo.EventName
              (NicePrint.outputTy denv) rty
            DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)

        // F# and .NET properties
        | Item.Property(_,pinfos) -> 
            let os = StringBuilder()
            let pinfo = List.head pinfos 
            let rty = pinfo.PropertyType(amap,m) 
            let _, rty, _cxs= PrettyTypes.PrettifyTypes1 g rty
            let rty = if pinfo.IsIndexer then mkTupledTy g (List.map snd (pinfo.ParamNamesAndTypes(amap, m))) --> rty else  rty 
            bprintf os "%s %a.%s: %a" (FSComp.SR.typeInfoProperty())
                (NicePrint.outputTyconRef denv) (tcrefOfAppTy g pinfo.EnclosingType)
                pinfo.PropertyName  
                (NicePrint.outputTy denv) rty

            let xml = GetXmlComment (if (pinfo.IsInThisAssembly infoReader.g.compilingFslib) then pinfo.XmlDoc else XmlDoc [||]) infoReader m d 

            DataTipElement(os.ToString(), xml)

        // F# constructors
        | Item.CtorGroup(_,minfos) 
        | Item.MethodGroup(_,minfos) ->
            FormatOverloadsToList infoReader m denv d minfos
        
        // The 'fake' zero-argument constructors of .NET structs 
        | Item.FakeInterfaceCtor typ ->
           let os = StringBuilder()
           let _, typ, _cxs = PrettyTypes.PrettifyTypes1 g typ
           bprintf os "%a" (NicePrint.outputTyconRef denv) (tcrefOfAppTy g typ)
           DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)
        
        // The 'fake' representation of constructors of .NET delegate types
        | Item.DelegateCtor delty -> 
           let os = StringBuilder()
           let _, delty, _cxs = PrettyTypes.PrettifyTypes1 g delty
           let _, _, _, fty = GetSigOfFunctionForDelegate infoReader delty m AccessibleFromSomewhere
           bprintf os "%a(%a)" (NicePrint.outputTyconRef denv) (tcrefOfAppTy g delty) (NicePrint.outputTy denv) fty;
           DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)

        // Types.
        | Item.Types(_,((TType_app(tcref,_) as typ):: _)) -> 
            let os = StringBuilder()
            bprintf os "%a" (NicePrint.outputTycon denv) tcref.Deref;
            OutputFullTypeName pubpath_of_tcref fullDisplayTextOfTyconRef os tcref;
            OutputUsefulTypeInfo infoReader m denv os typ;

            let xml = GetXmlComment (if (tyconRefUsesLocalXmlDoc g.compilingFslib tcref) then tcref.XmlDoc else XmlDoc [||]) infoReader m d 
            DataTipElement(os.ToString(), xml)

        // F# Modules and namespaces
        | Item.ModuleOrNamespaces((modref :: _) as modrefs) -> 
            let os = StringBuilder()
            let modrefs = modrefs |> RemoveDuplicateModuleRefs
            let definiteNamespace = modrefs |> List.forall (fun modref -> modref.IsNamespace)
            let kind = 
                if definiteNamespace then FSComp.SR.typeInfoNamespace()
                elif modrefs |> List.forall (fun modref -> modref.IsModule) then FSComp.SR.typeInfoModule()
                else FSComp.SR.typeInfoNamespaceOrModule()
            bprintf os "%s %s" kind (if definiteNamespace then fullDisplayTextOfModRef modref else modref.DemangledModuleOrNamespaceName)
            if not definiteNamespace then
                let namesToAdd = modrefs |> Seq.fold (fun st modref -> 
                    match fullDisplayTextOfParentOfModRef modref with 
                    | Some(txt) -> txt::st | _ -> st) [] |> Seq.mapi (fun i x -> i,x) |> Seq.toList
                if nonNil namesToAdd then 
                    bprintf os "\n"
                for i, txt in namesToAdd do
                    bprintf os "\n%s" ((if i = 0 then FSComp.SR.typeInfoFromFirst else FSComp.SR.typeInfoFromNext) txt)
                let xml = GetXmlComment (if (entityRefInThisAssembly g.compilingFslib modref) then modref.XmlDoc else XmlDoc [||]) infoReader m d 
                DataTipElement(os.ToString(), xml)
            else
                DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)
        // Named parameters
        | Item.ArgName(id) -> 
            let os = StringBuilder()
            //let _,typ,tpcs = PrettyTypes.PrettifyTypes1 typ
            bprintf os "%s %s" (FSComp.SR.typeInfoArgument()) id.idText; // (NicePrint.outputTy denv) typ
            DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)
            
        | Item.PropName(id) -> 
            let os = StringBuilder()
            bprintf os "%s %s" (FSComp.SR.typeInfoProperty()) id.idText; // (NicePrint.outputTy denv) typ
            DataTipElement(os.ToString(), GetXmlComment (XmlDoc [||]) infoReader m d)
        |  _ -> DataTipElementNone


    // Format the return type of an item
    let FormatItemReturnTypeToBuffer (infoReader:InfoReader) m denv os d = 
        let g = infoReader.g
        let amap = infoReader.amap
        let denv = {SimplerDisplayEnv denv with useColonForReturnType=true}
        match d with
        | Item.Value vref -> 
            let _, tau = vref.TypeScheme
            (* Note: prettify BEFORE we strip to make sure params look the same as types *)
            if isFunTy g tau then 
              let dtau,rtau = destFunTy g tau
              let ptausL,tpcsL = NicePrint.typesAndConstraintsL denv [dtau;rtau]
              let _,prtauL = List.frontAndBack ptausL
              bprintf os ": %a %a" bufferL prtauL bufferL tpcsL
            else
              bufferL os (NicePrint.topTypAndConstraintsL denv [] tau) 
        | Item.UnionCase(ucr) -> 
            let rty = generalizedTyconRef ucr.TyconRef
            bprintf os "%a" (NicePrint.outputTy denv) rty
        | Item.ActivePatternCase(apref) -> 
            let v = apref.ActivePatternVal
            let _, tau = v.TypeScheme
            let _, res = stripFunTy g tau
            let apinfo = the (TryGetActivePatternInfo v)
            let apnames = apinfo.Names
            let aparity = apnames.Length
            
            let rty = if aparity <= 1 then res else List.nth (argsOfAppTy g res) apref.CaseIndex
            bprintf os "%a" (NicePrint.outputTy denv) rty
        | Item.ExnCase _ -> 
            bufferL os (NicePrint.topTypAndConstraintsL denv [] g.exn_ty) 
        | Item.RecdField(rfinfo) ->
            bufferL os (NicePrint.topTypAndConstraintsL denv [] rfinfo.FieldType);
        | Item.ILField(finfo) ->
            bufferL os (NicePrint.topTypAndConstraintsL denv [] (finfo.FieldType(amap,m)))
        | Item.Event(einfo) ->
            bufferL os (NicePrint.topTypAndConstraintsL denv [] (PropTypOfEventInfo infoReader m AccessibleFromSomewhere einfo))
        | Item.Property(_,pinfos) -> 
            let pinfo = List.head pinfos
            let rty = pinfo.PropertyType(amap,m) 
            let layout = (NicePrint.topTypAndConstraintsL denv [] rty)
            bufferL os layout
        | Item.MethodGroup(_,(minfo :: _)) 
        | Item.CtorGroup(_,(minfo :: _)) -> 
            let rty = FSharpReturnTyOfMeth amap m minfo minfo.FormalMethodInst
            bufferL os (NicePrint.topTypAndConstraintsL denv [] rty) 
        | Item.FakeInterfaceCtor typ 
        | Item.DelegateCtor typ -> 
           bufferL os (NicePrint.topTypAndConstraintsL denv [] typ) 
            
        | _ -> ()

    let GetF1Keyword d : string option = 
        let rec unwindTypeAbbrev (tcref : TyconRef) =
            match tcref.TypeAbbrev with
            |   None -> Some tcref
            |   Some typ ->
                    match typ with
                    |   TType_app(tcref, _) -> unwindTypeAbbrev tcref
                    |   _ -> None
        
        let getKeywordForVRef (vref : ValRef) =
            let v = vref.Deref
            if v.IsModuleBinding then
                let tyconRef = v.TopValActualParent
                let paramsString =
                    match v.Typars with
                    |   [] -> ""
                    |   l -> "``"+(List.length l).ToString() 
                
                sprintf "%s.%s%s" (tyconRef |> ticksAndArgCountTextOfTyconRef) v.CompiledName paramsString |> Some
            else
                None
             
        match d with
        | Item.Value v -> getKeywordForVRef v
        | Item.ActivePatternCase apref -> apref.ActivePatternVal |> getKeywordForVRef

        | Item.UnionCase(ucr) -> 
            (ucr.TyconRef |> ticksAndArgCountTextOfTyconRef)+"."+ucr.Name |> Some

        | Item.RecdField rfi -> 
            (rfi.TyconRef |> ticksAndArgCountTextOfTyconRef)+"."+rfi.Name |> Some
        
        | Item.ILField finfo ->   
             match finfo with 
             | ILFieldInfo(tinfo, fdef) -> 
                 (tinfo.TyconRef |> ticksAndArgCountTextOfTyconRef)+"."+fdef.Name |> Some
        | Item.Types(_,((TType_app(tcref,_)) :: _)) 
        | Item.DelegateCtor(TType_app(tcref,_))
        | Item.FakeInterfaceCtor(TType_app(tcref,_))
        | Item.UnqualifiedType (tcref::_)
        | Item.ExnCase tcref -> 
            unwindTypeAbbrev tcref |> Option.map ticksAndArgCountTextOfTyconRef 

        // Pathological cases of the above
        | Item.Types _ 
        | Item.DelegateCtor _
        | Item.FakeInterfaceCtor _
        | Item.UnqualifiedType []
            -> None

        | Item.ModuleOrNamespaces(modref :: _) -> 
                    modref.Deref.CompiledRepresentationForTyrepNamed.FullName |> Some
        | Item.ModuleOrNamespaces [] -> None // Pathological case of the above

        | Item.Property(_,(pinfo :: _)) -> 
                match pinfo with 
                | FSProp(_, _, Some vref, _) 
                | FSProp(_, _, _, Some vref) -> 
                   // per spec, extension members in F1 keywords are qualified with definition class
                   match vref.ActualParent with 
                   | Parent tcref ->
                        (tcref |> ticksAndArgCountTextOfTyconRef)+"."+vref.PropertyName|> Some                     
                   | ParentNone -> None

                | ILProp(_, (ILPropInfo(tinfo,pdef))) -> 
                   let tcref = tinfo.TyconRef
                   (tcref |> ticksAndArgCountTextOfTyconRef)+"."+pdef.Name |> Some
                | FSProp _ -> None
        | Item.Property(_,[]) -> None // Pathological case of the above
                   
        | Item.Event einfo -> 
            match einfo with 
            | ILEvent(_,ilEventInfo)  ->
                let tinfo = ilEventInfo.ILTypeInfo
                let tcref = tinfo.TyconRef 
                (tcref |> ticksAndArgCountTextOfTyconRef)+"."+einfo.EventName |> Some
            | FSEvent(_,pinfo,_,_) ->
                match pinfo with 
                | FSProp(_, _, Some vref, _) 
                | FSProp(_, _, _, Some vref) -> 
                   // per spec, extension members in F1 keywords are qualified with definition class
                   match vref.ActualParent with 
                   | Parent tcref ->
                        (tcref |> ticksAndArgCountTextOfTyconRef)+"."+vref.PropertyName|> Some                     
                   | ParentNone -> None
                | _ -> None
        | Item.CtorGroup(_,minfos) ->
            match minfos with 
            | [] -> None
            | FSMeth(_, _, vref, _) :: _ ->
                   // per spec, extension members in F1 keywords are qualified with definition class
                   match vref.ActualParent with
                   | Parent tcref ->
                        (tcref |> ticksAndArgCountTextOfTyconRef)+"."+".#ctor"|> Some
                   | ParentNone -> None
            | (ILMeth (_,ILMethInfo(tinfo,_,_,_),_)) :: _ -> 
                       (tinfo.TyconRef |> ticksAndArgCountTextOfTyconRef)+".#ctor" |> Some
            | (DefaultStructCtor _ :: _) -> None                    
        | Item.MethodGroup(_,minfos) -> 
            match minfos with 
            | [] -> None
            | FSMeth(_, _, vref, _) :: _ ->
                   match vref.ActualParent with
                   | Parent tcref ->
                        (tcref |> ticksAndArgCountTextOfTyconRef)+"."+vref.CompiledName|> Some
                   | ParentNone -> None
                
            | (ILMeth (_,ILMethInfo(tinfo,isExt,mdef,_),_)) :: _ -> 
                    let typeString =
                        match isExt with
                        |    None -> tinfo.TyconRef |> ticksAndArgCountTextOfTyconRef
                        |    Some iltyperef -> 
                                // Extension methods cannot appear in generic classes, so we do not need any ticks
                                iltyperef.Name
                    let paramString =
                        let nGenericParams = mdef.GenericParams.Length 
                        if nGenericParams > 0 then "``"+(nGenericParams.ToString()) else ""
                    sprintf "%s.%s%s" typeString mdef.Name paramString |> Some

            | (DefaultStructCtor _ :: _) -> None
        |  Item.NewDef _ // "let x$yz = ..." - no keyword
        |  Item.ArgName _ // no keyword on named parameters 
        |  Item.PropName _
        |  Item.ImplicitOp _
        |  Item.ActivePatternResult _ // "let (|Foo|Bar|) = .. Fo$o ..." - no keyword
            ->  None



    let FormatDescriptionOfItem (infoReader:InfoReader)  m denv d : DataTipElement = 
        ErrorScope.Protect m 
            (fun () -> FormatItemDescriptionToDataTipElement infoReader m denv d)
            (fun err -> DataTipElementCompositionError(err))
        
    let FormatReturnTypeOfItem (infoReader:InfoReader) m denv d = 
        ErrorScope.Protect m (fun () -> bufs (fun buf -> FormatItemReturnTypeToBuffer infoReader m denv buf d)) (fun err -> err)

    // Compute the index of the VS glyph shown with an item in the Intellisense menu
    let GlyphOfItem(denv,d) = 

         /// Find the glyph for the given representation.    
         let ReprToGlyph(repr) = 
            match repr with
            | TFsObjModelRepr om -> 
                match om.fsobjmodel_kind with 
                | TTyconClass -> iIconGroupClass
                | TTyconInterface -> iIconGroupInterface
                | TTyconStruct -> iIconGroupStruct
                | TTyconDelegate _ -> iIconGroupDelegate
                | TTyconEnum _ -> iIconGroupEnum
            | TRecdRepr _ -> iIconGroupType
            | TFiniteUnionRepr _ -> iIconGroupUnion
            | TILObjModelRepr(_,_,{tdKind=kind}) -> 
                match kind with 
                | ILTypeDefKind.Class -> iIconGroupClass
                | ILTypeDefKind.ValueType -> iIconGroupStruct
                | ILTypeDefKind.Interface -> iIconGroupInterface
                | ILTypeDefKind.Enum -> iIconGroupEnum
                | ILTypeDefKind.Delegate -> iIconGroupDelegate
                | ILTypeDefKind.Other _ -> iIconGroupTypedef
            | TAsmRepr _ -> iIconGroupTypedef
            | TMeasureableRepr _-> iIconGroupTypedef
         
         /// Find the glyph for the given type representation.
         let rec TypToGlyph(typ) = 
            if isAppTy denv.g typ then 
                let tcref = tcrefOfAppTy denv.g typ
                tcref.TypeReprInfo |> (function None -> iIconGroupClass | Some repr -> ReprToGlyph repr)
            elif isTupleTy denv.g typ then iIconGroupStruct
            elif isFunction denv.g typ then iIconGroupDelegate
            elif isTyparTy denv.g typ then iIconGroupStruct
            else iIconGroupTypedef

            
         /// Find the glyph for the given value representation.
         let ValueToGlyph(typ) = 
            if isFunction denv.g typ then iIconGroupMethod
            else iIconGroupConstant
              
         /// Find the major glyph of the given named item.       
         let NamedItemToMajorGlyph item = 
            // This may explore assemblies that are not in the reference set,
            // e.g. for type abbreviations to types not in the reference set. 
            // In this case just use iIconGroupClass.
           protectAssemblyExploration  iIconGroupClass (fun () ->
              match item with 
              | Item.Value(vref) -> ValueToGlyph(vref.Type)
              | Item.Types(_,typ::_) -> TypToGlyph(stripTyEqns denv.g typ)    
              | Item.UnionCase _
              | Item.ActivePatternCase _ -> iIconGroupEnumMember   
              | Item.ExnCase _ -> iIconGroupException   
              | Item.RecdField _ -> iIconGroupFieldBlue   
              | Item.ILField _ -> iIconGroupFieldBlue    
              | Item.Event _ -> iIconGroupEvent   
              | Item.Property _ -> iIconGroupProperty   
              | Item.CtorGroup _ 
              | Item.DelegateCtor _ 
              | Item.FakeInterfaceCtor _
              | Item.MethodGroup _  -> iIconGroupMethod   
              | Item.Types _ -> iIconGroupClass   
              | Item.ModuleOrNamespaces(modref::_) -> 
                    if modref.IsNamespace then iIconGroupNameSpace else iIconGroupModule
              | Item.ArgName _ -> iIconGroupVariable
              | Item.PropName _ -> iIconGroupVariable
              | _ -> iIconGroupError)

         /// Find the minor glyph of the given named item.       
         let NamedItemToMinorGlyph item = 
            // This may explore assemblies that are not in the reference set,
            // e.g. for type abbreviations to types not in the reference set. 
            // In this case just use iIconItemNormal.
           protectAssemblyExploration  iIconItemNormal (fun () ->
             match item with 
              | Item.Value(vref) when isFunction denv.g vref.Type -> iIconItemSpecial
              | _ -> iIconItemNormal)

         (6 * NamedItemToMajorGlyph(d)) + NamedItemToMinorGlyph(d)

     
    let string_is_prefix_of m n  = String.length n >= String.length m && String.sub n 0 (String.length m) = m



open ItemDescriptions

//----------------------------------------------------------------------------
// Scopes etc. for intellisense
//--------------------------------------------------------------------------

type Names = string list 
type NamesWithResidue = Names * string 


//----------------------------------------------------------------------------
// Declarations
//----------------------------------------------------------------------------

          
/// An intellisense declaration
[<Sealed>]
type Declaration(name, description:Lazy<DataTipText>, glyph:int, syncop:(unit->unit)->unit) =
    let mutable descriptionText:DataTipText option = None
    member decl.Name = name
    member decl.DescriptionText = 
        match descriptionText with
        | Some(descriptionText) -> descriptionText
        | None ->
            // syncop "Synchronous Operation" causes the lambda to execute inside the background compiler.
            syncop (fun () -> descriptionText<-Some(Lazy.force description))
            descriptionText.Value
    member decl.Glyph = glyph      
      
/// A table of declarations for Intellisense completion 
[<Sealed>]
type Decls(d: Declaration array) = 

    member self.Items = d  
    
    member self.Count = 
        Array.length d

    member self.Name i : string = 
        d.[i].Name

    member self.Description i : DataTipText = 
        ErrorScope.Protect Range.range0 (fun () -> d.[i].DescriptionText) (fun err -> DataTipText([DataTipElementCompositionError err]))

    member self.Glyph i = d.[i].Glyph
            
    // Make a 'Declarations' object for a set of selected items
    static member Create(infoReader:InfoReader,m,denv,items, syncop:(unit->unit)->unit) = 
        let g = infoReader.g
         
        // Remove all duplicates first
        let items = items |> RemoveDuplicateItems g
        let items = items |> RemoveExplicitlySuppressed g
        
        // Bag by name using multi-maps 
        let items = items |> List.map (fun d -> DisplayNameOfItem g d, d) 
        let items = List.foldBack (fun (n,d) acc -> NameMultiMap.add n d  acc) items NameMultiMap.empty 

        // Prefer to show types first - they usually have very useful XmlDocs 
        let items = items |> NameMap.toList 
        let items = 
            items 
            |> List.map (fun (nm,bag) -> 
              nm,
              bag |> List.sortWith (fun a b -> 
                match a,b with  
                | Item.CtorGroup _, Item.Types _ -> 1
                | Item.Types _, Item.CtorGroup _ -> -1
                | _ -> 0))
              
        // Sort by name 
        let items = items |> List.sortBy fst
        if verbose then dprintf "service.ml: mkDecls: %d found after filtering\n" (List.length items); 
        let decls = 
        // Filter out operators
            let items = items |> List.filter (fun (nm,_) -> not (IsOpName nm) || (IsActivePatternName nm)) 
            
            // Filter out duplicate names
            items |> List.map (fun (nm,itemsWithSameName) -> 
                match itemsWithSameName with
                | [] -> failwith "Unexpected empty bag"
                | items -> 
                    let desc = lazy (DataTipText(items |> List.map (FormatDescriptionOfItem infoReader m denv)))
                    Declaration(nm,desc,GlyphOfItem(denv,List.head items),syncop))

        new Decls(Array.ofList decls)

      
//----------------------------------------------------------------------------
// Methods
//--------------------------------------------------------------------------


type Param = 
    { Name: string;
      Display: string;
      Description: string }

/// Format parameters for Intellisense completion
module internal Params = 
    let param_of_typL tyL =
        { Name= "";
          Display =  Layout.showL tyL;
          Description = "" }

    let ParamOfRecdField denv f =
        { Name= "" (* f.rfield_id.idText *) ;
          Display = NicePrint.prettyStringOfTy denv f.rfield_type;
          Description = "" }
    let ParamOfParamData  denv paramData =
        { Name= ""; //(match pname with None -> "" | Some pn -> pn);
          Display = stringOfParamData denv paramData;
          Description = "" }

    let ParamsOfParamDatas  denv (paramDatas:ParamData list) rty = 
        let paramPrefixes,paramTypes = 
            paramDatas 
            |> List.map (fun (ParamData(_,_,optArgInfo,nmOpt,pty)) -> 
                let isOptArg = optArgInfo.IsOptional
                match nmOpt, isOptArg, tryDestOptionTy denv.g pty with 
                // Layout an optional argument 
                | Some(nm), true, Some(pty) -> 
                    (sprintf "?%s:" nm),  pty
                // Layout an unnamed argument 
                | None, _,_ -> 
                    "", pty;
                // Layout a named argument 
                | Some nm,_,_ -> 
                    (sprintf "%s: " nm),pty)
            |> List.unzip
        let paramTypeAndRetLs,_ = NicePrint.typesAndConstraintsL denv (paramTypes@[rty])
        let paramTypeLs,_ = List.frontAndBack  paramTypeAndRetLs
        (paramPrefixes,paramTypeLs) ||> List.map2 (fun paramPrefix tyL -> 
            { Name= ""; 
              Display = paramPrefix+(showL tyL);
              Description = "" })

    let ParamsOfTypes denv args rtau = 
        (*let arg,rtau = destFunTy rtau 
        let args = tryDestTupleTy arg  *)
        
        // Review, use tpcsL here
        let ptausL, _ = NicePrint.typesAndConstraintsL denv (args@[rtau]) 
        let argsL,_ = List.frontAndBack ptausL 
        List.map param_of_typL argsL

    let ParamsOfItem (infoReader:InfoReader) m denv d = 
        let amap = infoReader.amap
        match d with
        | Item.Value vref -> 
            let _, tau = vref.TypeScheme
            if isFunTy denv.g tau then 
                let arg,rtau = destFunTy denv.g tau 
                let args = tryDestTupleTy denv.g arg 
                ParamsOfTypes denv args rtau
            else []
        | Item.UnionCase(ucr)   -> ucr.UnionCase.RecdFields |> List.map (ParamOfRecdField denv) 
        | Item.ActivePatternCase(apref)   -> 
            let v = apref.ActivePatternVal 
            let _,tau = v.TypeScheme
            let args, _ = stripFunTy denv.g tau 
            ParamsOfTypes denv args tau
        | Item.ExnCase(ecref)     -> 
            ecref |> recdFieldsOfExnDefRef |> List.map (ParamOfRecdField denv) 
        | Item.Property(_,pinfo :: _) -> 
            let paramDatas = 
                pinfo.ParamNamesAndTypes(amap,m)
                |> List.map (fun (nm,pty) -> ParamData(false,false,NotOptional,nm, pty))
            
            let rty = pinfo.PropertyType(amap,m) 
            ParamsOfParamDatas denv paramDatas rty
        | Item.CtorGroup(_,(minfo :: _)) 
        | Item.MethodGroup(_,(minfo :: _)) -> 
            let paramDatas = ParamDatasOfMethInfo amap m minfo minfo.FormalMethodInst |> List.head
            let rty = FSharpReturnTyOfMeth amap m minfo minfo.FormalMethodInst 
            ParamsOfParamDatas denv paramDatas rty
        | Item.FakeInterfaceCtor _ -> []
        | Item.DelegateCtor delty -> 
            let _, _, _, fty = GetSigOfFunctionForDelegate infoReader delty m AccessibleFromSomeFSharpCode
            ParamsOfParamDatas denv [ParamData(false,false,NotOptional,None, fty)] delty
        |  _ -> []


/// A single method for Intellisense completion
[<NoEquality; NoComparison>]
type internal Method = 
    { Description: DataTipText; 
      Type: string;
      Parameters: Param array }


/// A table of methods for Intellisense completion
[<Sealed>]
type MethodOverloads( name: string, methods: Method array) = 
    member x.Name = name
    member x.Methods = methods

    member meths.Count = 
        try 
            methods.Length
        with | e -> 
            Trace.PrintLine("CompilerServices", fun _ -> sprintf "Unexpected error (Methods.getCount): %s\n" (e.ToString())); 
            0

    member meths.Description i : DataTipText = 
        ErrorScope.Protect Range.range0 (fun () -> methods.[i].Description) (fun err -> DataTipText([DataTipElementCompositionError err]))
 
    member meths.Type i : string = 
        ErrorScope.ProtectWithDefault Range.range0 (fun () -> methods.[i].Type) ""

    member meths.ParameterCount i = 
        ErrorScope.ProtectWithDefault Range.range0 (fun () -> methods.[i].Parameters.Length) 0

    member meths.ParameterInfo i j : Param = 
        ErrorScope.Protect Range.range0 (fun () -> methods.[i].Parameters.[j]) (fun err -> { Name=  "";Display =  "";Description =  err })

    static member Create(infoReader:InfoReader,m,denv,items) = 
        let g = infoReader.g
        if verbose then dprintf "mkMethods: %d items on input\n" (List.length items); 
        if isNil items then new MethodOverloads("", [| |]) else
        let name = DisplayNameOfItem g (List.head items) 
        let items = 
          items |> List.collect (fun item -> 
               match item with 
               | Item.MethodGroup(nm,minfos) -> List.map (fun minfo -> Item.MethodGroup(nm,[minfo])) minfos 
               | Item.CtorGroup(nm,cinfos) -> List.map (fun minfo -> Item.CtorGroup(nm,[minfo])) cinfos 
               | Item.FakeInterfaceCtor _
               | Item.DelegateCtor _ -> [item]
               | Item.NewDef _ 
               | Item.ILField _ -> []
               | Item.Event _ -> []
               | Item.RecdField(rfinfo) -> 
                   if isFunction g rfinfo.FieldType then [item] else []
               | Item.Value v -> 
                   if isFunction g v.Type then [item] else []
               | Item.UnionCase(ucr) -> 
                   if not ucr.UnionCase.IsNullary then [item] else []
               | Item.ExnCase(ecr) -> 
                   if recdFieldsOfExnDefRef ecr |> nonNil then [item] else []
               | Item.Property(_,pinfos) -> 
                   let pinfo = List.head pinfos 
                   if pinfo.IsIndexer then [item] else []
               | _ -> [])

        if verbose then dprintf "mkMethods: %d items after filtering for methodness\n" (List.length items);         

        let methods = 
            items |> Array.ofList |> Array.map (fun item -> 
                { Description= DataTipText([FormatDescriptionOfItem infoReader m denv item]);
                  Type= (FormatReturnTypeOfItem infoReader m denv item);
                  Parameters = Array.ofList (Params.ParamsOfItem infoReader m denv item) })

        new MethodOverloads(name, methods)

//----------------------------------------------------------------------------
// Scopes. 
//--------------------------------------------------------------------------

type Position = int * int
type Range = Position * Position
                                                                             
[<NoEquality; NoComparison>]
type FindDeclResult = 
    ///  no identifier at this locn 
    | IdNotFound    
    /// no decl info in this buffer at the moment 
    | NoDeclInfo   
    /// found declaration; return (position-in-file, name-of-file, names-of-referenced-assemblies)
    | DeclFound      of Position * string * (string list)
    /// found declaration but source file doesn't exist; try to generate an .fsi
    | NeedToGenerate of string * (string -> string) * (string list)


/// This type is used to describe what was found during the name resolution.
/// (Depending on the kind of the items, we may stop processing or continue to find better items)
[<RequireQualifiedAccess>]
[<NoEquality; NoComparison>]
type internal NameResResult = 
    | Members of (Item list * DisplayEnv * range)
    | Cancel of DisplayEnv * range
    | Empty
    
    
// A scope represents everything we get back from the typecheck of a file.
// It acts like an in-memory database about the file.
// It is effectively immutable and not updated: when we re-typecheck we just drop the previous
// scope object on the floor and make a new one.
[<Sealed>]
type Scope(/// Information corresponding to miscellaneous command-line options (--define, etc).
           sTcConfig: Build.TcConfig,
           g: Env.TcGlobals,
           /// AssemblyName -> IL-Module 
           amap: Import.ImportMap,
           /// project directory, or directory containing the file that generated this scope if no project directory given 
           sProjectDir: string ,
           sFile:string,
           /// Name resolution environments for every interesting region in the file. These regions may
           /// overlap, in which case the smallest region applicable should be used.
           sEnvs: ResizeArray<range * Nameres.NameResolutionEnv * AccessorDomain>,
           /// This is a name resolution environment to use if no better match can be found.
           sFallback:Nameres.NameResolutionEnv,
           /// Information of exact types found for expressions, that can be to the left of a dot.
           /// Also for exact name resolutions
           /// pos -- line and column
           /// typ - the inferred type for an expression
           /// Item -- named item
           /// DisplayEnv -- information about printing. For example, should redundant keywords be hidden?
           /// NameResolutionEnv -- naming environment--for example, currently open namespaces.
           /// range -- the starting and ending position      
           capturedExprTypings: ResizeArray<(pos * TType * DisplayEnv * Nameres.NameResolutionEnv * AccessorDomain * range)>,
           capturedNameResolutions: ResizeArray<(pos * Item * ItemOccurence * DisplayEnv * Nameres.NameResolutionEnv * AccessorDomain * range)>,
           loadClosure : LoadClosure option,
           syncop:(unit->unit)->unit) = 

    // These strings are potentially large and the editor may choose to hold them for a while.
    // Use this cache to fold together data tip text results that are the same. 
    // Is not keyed on 'Names' collection because this is invariant for the current position in 
    // this unchanged file. Keyed on lineStr though to prevent a change to the currently line
    // being available against a stale scope.
    let getDataTipTextCache = AgedLookup<int*int*string,DataTipText>(recentForgroundTypeCheckLookupSize,areSame=(fun (x,y) -> x = y))
    
    let infoReader = new InfoReader(g,amap)
    let ncenv = new NameResolver(g,amap,infoReader,Nameres.FakeInstantiationGenerator)
    
    // Visual Studio uses line counts starting at 0, F# uses them starting at 1 
    let mkPos line idx = mkPos (line+1) idx 

    /// Find the most precise naming environment for the given line and column
    let GetBestEnvForPos cursorPos  =
        
        let bestSoFar = ref None

        // Find the most deeply nested enclosing scope 
        sEnvs |> ResizeArray.iter (fun (possm,env,ad) -> 
            Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "Examining range %s for strict inclusion\n" (stringOfRange possm))
            if rangeContainsPos possm cursorPos then
                match !bestSoFar with 
                | Some (bestm,_,_) -> 
                    if rangeContainsRange bestm possm then 
                      bestSoFar := Some (possm,env,ad)
                | None -> 
                    bestSoFar := Some (possm,env,ad));

        let mostDeeplyNestedEnclosingScope = !bestSoFar 
        
        match mostDeeplyNestedEnclosingScope with 
        | Some (m,_,_) -> Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "Strict Inclusion found env for range %s\n" (stringOfRange m))
        | None ->Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "Strict Inclusion found no environment, cursorPos = %s\n" (stringOfPos cursorPos))
        
        // Look for better subtrees on the r.h.s. of the subtree to the left of where we are 
        // Should really go all the way down the r.h.s. of the subtree to the left of where we are 
        // This is all needed when the index is floating free in the area just after the environment we really want to capture 
        // We guarantee to only refine to a more nested environment.  It may not be strictly  
        // the right environment, but will alwauys be at least as rich 

        let bestAlmostIncludedSoFar = ref None 

        sEnvs |> ResizeArray.iter (fun (possm,env,ad) -> 
            if rangeBeforePos possm cursorPos then 
                let contained = 
                    match mostDeeplyNestedEnclosingScope with 
                    | Some (bestm,_,_) -> rangeContainsRange bestm possm 
                    | None -> true 
                
                if contained then 
                    match  !bestAlmostIncludedSoFar with 
                    | Some (rightm:range,_,_) -> 
                        if posGt possm.End rightm.End || 
                          (posEq possm.End rightm.End && posGt possm.Start rightm.Start) then
                            bestAlmostIncludedSoFar := Some (possm,env,ad)
                    | _ -> bestAlmostIncludedSoFar := Some (possm,env,ad));

        let resEnv = 
            match !bestAlmostIncludedSoFar with 
            | Some (m,env,ad) -> 
                Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "Chose refined-rightmost env covering range %s\n" (stringOfRange m))
                env,ad
            | None -> 
                match mostDeeplyNestedEnclosingScope with 
                | Some (m,env,ad) -> 
                    Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "Chose refined env covering range %s\n" (stringOfRange m))
                    env,ad
                | None -> 
                    Trace.PrintLine("CompilerServicesVerbose", fun () -> "Using fallback global env\n")
                    (sFallback,AccessibleFromSomeFSharpCode)
        let pm = mkRange sFile cursorPos cursorPos 

        resEnv,pm

    /// The items that come back from ResolveCompletionsInType are a bit
    /// noisy. Filter a few things out.
    ///
    /// e.g. prefer types to constructors for DataTipText 
    let FilterItemsForCtors filterCtors items = 
        let items = items |> List.filter (function (Item.CtorGroup _) when filterCtors = ResolveTypeNamesToTypeRefs -> false | _ -> true) 
        items
        
    /// Looks at the exact name resolutions that occurred during type checking
    /// If 'membersByResidue' is specified, we look for members of the item obtained 
    /// from the name resultion and fitler them by the specified residue (?)
    let GetPreciseItemsFromNameResolution(line,colAtEndOfNames,membersByResidue,filterCtors) = 
        let endOfNamesPos = mkPos line colAtEndOfNames
        Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseItemsFromNameResolution: line = %d, colAtEndOfNames = %d, endOfNamesPos = %s\n" line colAtEndOfNames (stringOfPos endOfNamesPos))

        let quals = 
            capturedNameResolutions 
            |> ResizeArray.filter (fun (pos,_,_,_,_,_,_) -> 
                Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "Checking position %s = %s\n" (stringOfPos endOfNamesPos) (stringOfPos pos))
                posEq pos endOfNamesPos)
        Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseItemsFromNameResolution: Found %d relevant quals\n" quals.Count)
        
        let items = quals |> ResizeArray.toList         
        
        // Filter items to show only valid & return Some if there are any
        let returnItemsOfType items g denv m f =
            let items = items |> RemoveDuplicateItems g
            let items = items |> RemoveExplicitlySuppressed g
            let items = items |> FilterItemsForCtors filterCtors
            if nonNil items then
                Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseItemsFromNameResolution: Results in %d items!\n" items.Length)
                f(items, denv, m) 
            else NameResResult.Empty        
      
        match items, membersByResidue with 
        
        // If we're looking for members using a residue, we'd expect only
        // a single item (pick the first one) and we need the residue (which may be "")
        | (_,Item.Types(_,(typ::_)),_,denv,nenv,ad,m)::_, Some _ -> 
            let items = ResolveCompletionsInType ncenv nenv m ad true typ 
            returnItemsOfType items g denv m NameResResult.Members
        
        // Value reference from the name resolution. Primarilly to disallow "let x.$ = 1"
        // In most of the cases, value references can be obtained from expression typings or from environment,
        // so we wouldn't have to handle values here. However, if we have something like:
        //   let varA = "string"
        //   let varA = if b then 0 else varA.
        // then the expression typings get confused (thinking 'varA:int'), so we use name resolution even for usual values.
        
        | (_, Item.Value(vref), occurence, denv, nenv, ad, m)::_, Some _ ->
            if (occurence <> ItemOccurence.Use) then 
              // Return empty list to stop further lookup - for value declarations
              NameResResult.Cancel(denv, m)
            else 
              // If we have any valid items for the value, then return completions for its type now
              // adjust the type in case this is the 'this' pointer stored in a reference cell
              let ty = StripSelfRefCell(g, vref.BaseOrThisInfo, vref.TauType) 
              let items = ResolveCompletionsInType ncenv nenv m ad false ty
              returnItemsOfType items g denv m NameResResult.Members
        
        // No residue, so the items are the full resolution of the name
        
        // Grab the first resolution (note the capturedNameResolutions come in in reversed order: latest recorded comes 
        // first, so this will be the last name resolution recorded for this text in the file. This is used to update the 
        // resolution for an expression
        //     new Type
        // to an Item.CtorGroup rather than an Item.Types
        | (_,_,_,denv,_,_,m) :: _, None -> 
            Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseItemsFromNameResolution: No residue, precise results found\n")
            let items = items |> List.map (fun (_,item,_,_,_,_,_) -> item)
            let items = items |> RemoveDuplicateItems g
            let items = items |> RemoveExplicitlySuppressed g
            NameResResult.Members(items, denv,m)
        | _ , _ -> NameResResult.Empty


    /// Looks at the exact expression types at the position to the left of the 
    /// residue then the source when it was typechecked.
    let GetPreciseCompletionListFromExprTypings(line,colAtEndOfNames,filterCtors) = 
        let endOfExprPos = mkPos line colAtEndOfNames
        Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseCompletionListFromExprTypings: line = %d, colAtEndOfNames = %d, endOfExprPos = %s\n" line colAtEndOfNames (stringOfPos endOfExprPos))
        let quals = 
            capturedExprTypings 
            |> ResizeArray.filter (fun (pos,_,_,_,_,_) -> 
                Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "Checking position %s = %s\n" (stringOfPos endOfExprPos) (stringOfPos pos))
                posEq pos endOfExprPos) 
            |> ResizeArray.toList
        Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseCompletionListFromExprTypings: Found %d relevant quals\n" quals.Length)

        // Note the capturedExprTypings come in in reversed order (latest recorded comes first).
        // If expressions overlap we want to find the last one pushed. 
        // e.g. this happens with expressions of the form 
        //          (a + b) 
        // which are really 
        //          ((+) a) b
        // but where both applications are marked to be at the same position

        // Heuristic "not (isFunTy typ)":
        // Generally, we prefer qualItems since it is the result of resolving the particular thing we're dotting off of.
        //
        // However, qualItems isn't guaranteed to be accurate because it's the result of a type check on possibly incomplete
        // code. See bug 2584 for example.
        //
        // We can do a little better when the thing being dotted is thought to be a function by qualItems. In this case, dotting
        // the function will just show methods for System.Object anyway so we can return the best named environment instead since
        // it will also contain the methods for System.Object.

        quals |> List.iter (fun (_,typ,_,_,_,_)  -> 
            Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseCompletionListFromExprTypings: before filter, type '%s', is_typar_ty=%b, IsFromError=%b\n" (showL (typeL typ)) (isTyparTy g typ) (isTyparTy g typ && (destTyparTy g typ).IsFromError)))

        let quals = 
            quals |> List.filter (fun (_,typ,denv,_,_,_) -> 
                not (isFunTy denv.g typ) && 
                not (isTyparTy denv.g typ && (destTyparTy denv.g typ).IsFromError))

        quals |> List.iter (fun (_,typ,_,_,_,_)  -> 
            Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseCompletionListFromExprTypings: after filter, type '%s'\n" (showL (typeL typ))))

        match quals |> List.rev with
        | (_,typ,denv,nenv,ad,m) :: _  -> 
            let items = ResolveCompletionsInType ncenv nenv m ad false typ 
            Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetPreciseCompletionListFromExprTypings: Results in %d items!\n" items.Length)
            let items = items |> RemoveDuplicateItems g
            let items = items |> RemoveExplicitlySuppressed g
            let items = items |> FilterItemsForCtors filterCtors 
            Some(items,denv,m)
        | _ -> None

    /// Find items in the best naming environment.
    let GetEnvironmentLookupResolutions(line,colAtEndOfNamesAndResidue,plid,filterCtors,showObsolete) = 
        Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetEnvironmentLookupResolutions: line = %d, colAtEndOfNamesAndResidue = %d, plid = %+A, showObsolete = %b\n" line colAtEndOfNamesAndResidue plid showObsolete)
        let cursorPos = mkPos line colAtEndOfNamesAndResidue
        let (nenv,ad),m = GetBestEnvForPos cursorPos
        let items = Nameres.ResolvePartialLongIdent ncenv nenv m ad plid showObsolete
        let items = items |> RemoveDuplicateItems g 
        let items = items |> RemoveExplicitlySuppressed g
        let items = items |> FilterItemsForCtors filterCtors 
         
        Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "GetEnvironmentLookupResolutions: found %d item groups by looking up long identifier chain in environment\n" (List.length items))
        items, nenv.DisplayEnv,m 


    /// Resolve a location and/or text to items.
    //   Three techniques are used
    //        - look for an exact known name resolution from type checking
    //        - use the known type of an expression, e.g. (expr).Name, to generate an item list  
    //        - lookup an entire name in the name resolution environment, e.g. A.B.Name, to generate an item list
    //
    // The overall aim is to resolve as accurately as possible based on what we know from type inference
    
    let GetDeclItemsForNamesAtPosition(origLongIdentOpt: string list option,residueOpt,line,(lineStr:string),colAtEndOfNamesAndResidue,filterCtors) = 
        use t = Trace.Call("CompilerServices","GetDeclItemsForNamesAtPosition", fun _->sprintf " plid=%+A residueOpt=%+A line=%d colAtEndOfNames=%d" origLongIdentOpt (residueOpt:option<string>) line colAtEndOfNamesAndResidue)
                
        // Try to use the exact results of name resolution during type checking to generate the results
        // This is based on position (i.e. colAtEndOfNamesAndResidue). This is not used if a residueOpt is given.
        let nameResItems = 
            match residueOpt with 
            | None -> GetPreciseItemsFromNameResolution(line, colAtEndOfNamesAndResidue, None, filterCtors)
            | Some(residue) when 
                  (let loc = colAtEndOfNamesAndResidue - residue.Length - 1
                   loc >= 0 && loc < lineStr.Length && lineStr.[loc] = '.') -> 
                // for "Foo.B", get location at the end of Foo (or "Foo<int>")
                let colAtEndOfNames = max 0 (colAtEndOfNamesAndResidue - residue.Length - 1) 
                GetPreciseItemsFromNameResolution(line, colAtEndOfNames, Some(residue), filterCtors)
            | _ -> NameResResult.Empty
        
        // Normalize to form A.B.C.D where D is the residue. It may be empty for "A.B.C."
        let plid, residue = 
            match origLongIdentOpt, residueOpt with
            | None, _ -> [], ""
            | Some(origLongIdent), Some(r) -> origLongIdent,r
            | Some(origLongIdent), None ->
                assert (nonNil origLongIdent) 
                List.frontAndBack origLongIdent
                
        /// Post-filter items to make sure they have precisely the right name
        /// This also checks that there are some remaining results 
        let (|FilterRelevantItems|_|) ((items,denv,m) as orig) =

            // Return only items with the specified name
            let filterDeclItemsByResidue items = 
                items |> List.filter (fun item -> 
                    let n1 =  DisplayNameOfItem g item 
                    Trace.PrintLine("CompilerServicesVerbose", fun () -> sprintf "\nn1 = <<<%s>>>\nn2 = <<<%s>>>\n" n1 residue)
                    match item with
                    | Item.Types _ | Item.CtorGroup _ -> residue + "Attribute" = n1 || residue = n1
                    | _ -> residue = n1 )

            // Are we looking for items with precisely the given name?
            if origLongIdentOpt.IsSome && nonNil items && isNone residueOpt then
                Trace.PrintLine("CompilerServices", fun _ -> sprintf "looking through %d items before filtering by residue\n" (List.length items))       
                let items = items |> filterDeclItemsByResidue 
                if nonNil items then Some(items,denv,m) else None        
            else 
                // When (items = []) we must returns Some([],..) and not None
                // because this value is used if we want to stop further processing (e.g. let x.$ = ...)
                Some(orig)

        match nameResItems with            
        | NameResResult.Cancel(denv,m) -> Some([], denv, m)
        | NameResResult.Members(FilterRelevantItems(items)) -> 
            Trace.PrintLine("CompilerServices", fun _ -> sprintf "GetDeclItemsForNamesAtPosition: lookup based on name resolution results successful, #items = %d, exists ctor = %b\n" (p13 items).Length (items |> p13 |> List.exists (function Item.CtorGroup _ -> true | _ -> false)))       
            Some items
        | _ ->
        
        match origLongIdentOpt with
        | None -> None
        | Some _ -> 
            Trace.PrintLine("CompilerServices", fun _ -> sprintf "GetDeclItemsForNamesAtPosition: plid = %+A, residue = %+A, colAtEndOfNamesAndResidue = %+A\n" plid residue colAtEndOfNamesAndResidue)       

            // Try to use the type of the expression on the left to help generate a completion list
            let qualItems = 
                // we're looking for 'expr.residue'. Drop the residue and the "."
                if isNil plid then
                    None
                else
                    let colAtEndOfNames = max 0 (colAtEndOfNamesAndResidue - residue.Length - 1) 
                    GetPreciseCompletionListFromExprTypings(line,colAtEndOfNames,filterCtors)

            match qualItems with            
            | Some(FilterRelevantItems(items)) 
                    // Initially we only use the expression typings when looking up, e.g. (expr).Nam or (expr).Name1.Nam
                    // These come through as a long identifier with plid starting with "". Otherwise we try an environment lookup
                    // and then return to the qualItems. This is because the expression typings are a little inaccurate, primarily because
                    // it appears we're getting some typings recorded for non-atomic expressions like "f x"
                    when (match plid with "" :: _ -> true | _ -> false)  -> 
                Trace.PrintLine("CompilerServices", fun _ -> sprintf "GetDeclItemsForNamesAtPosition: lookup based on expression typings successful\n")       
                Some items
            | _ ->         
            // Use an environment lookup as the last resort
            
            // Do we need to look for obsolete modules & types? 
            // This will be true when we're after a dot and we want completions in an (otherwise) unseen module.
            // Note this doesn't mean that we want any unseen things (e.g. private) - only 'Obsolete'!
            // e.g.:   SomeModuleName.SomeObsoleteModule.<$>   ~> residueOpt = Some("")
            let showObsolete = residueOpt.IsSome
            // Account for the region around '.'
            let loc = 
                match colAtEndOfNamesAndResidue with
                | pastEndOfLine when pastEndOfLine >= lineStr.Length -> lineStr.Length
                | atDot when lineStr.[atDot] = '.' -> atDot + 1
                | atStart when atStart = 0 -> 0
                | otherwise -> otherwise - 1
            let envItems = GetEnvironmentLookupResolutions(line,loc,plid,filterCtors,showObsolete) 
            match nameResItems, envItems, qualItems with            
            
            // First, use unfiltered name resolution items, if they're not empty
            | NameResResult.Members(items, denv, m), _, _ when nonNil items -> 
                Trace.PrintLine("CompilerServices", fun _ -> sprintf "GetDeclItemsForNamesAtPosition: lookup based on name resolution results successful, #items = %d, exists ctor = %b\n" (items).Length (items |> List.exists (function Item.CtorGroup _ -> true | _ -> false)))       
                Some(items, denv, m)                
            
            // If we have nonempty items from environment that were resolved from a type, then use them... 
            // (that's better than the next case - here we'd return 'int' as a type)
            | _, FilterRelevantItems(items, denv, m), _ when nonNil items ->
                Trace.PrintLine("CompilerServices", fun _ -> sprintf "GetDeclItemsForNamesAtPosition: lookup based on name and environment successful\n")       
                Some(items, denv, m)

            // Try again with the qualItems
            | _, _, Some(FilterRelevantItems(items)) ->
                Some(items)
                
            | _ -> None

    member x.GetDeclarations line lineStr colAtEndOfNames (names,residue) : Decls =
        use t = Trace.Call("CompilerServices","GetDeclarations", fun _->sprintf " line=%+A,colAtEndOfNames=%+A,names=%+A" line colAtEndOfNames names)
        ErrorScope.Protect 
            Range.range0 
            (fun () -> 
                match GetDeclItemsForNamesAtPosition(Some(names),Some(residue),line,lineStr,colAtEndOfNames,ResolveTypeNamesToTypeRefs) with
                | None -> Decls [| |]
                | Some(items,denv,m) -> Decls.Create(infoReader,m,denv,items,syncop))
            (fun msg -> 
                Decls [| Declaration("<Note>", (lazy(DataTipText([DataTipElementCompositionError msg]))), 0, syncop) |] 
            )
            
    member scope.GetReferenceResolutionDataTipText(line,lineStr,col) : DataTipText = 
        let pos = Range.mkPos (line+1) col
        let LineIfExists(append) =
            if not(String.IsNullOrEmpty(append)) then append.Trim([|' '|])+"\n"
            else ""     
        let IsPosMatch(pos, ar:AssemblyReference) : bool = 
            let isRangeMatch = (Range.rangeContainsPos ar.Range pos) 
            let isNotSpecialRange = (ar.Range <> rangeStartup) && (ar.Range <> range0) && (ar.Range <> rangeCmdArgs)
            let isMatch = isRangeMatch && isNotSpecialRange
            isMatch                      

        ErrorScope.Protect 
            Range.range0 
            (fun () ->  
                let matches =
                    match loadClosure with
                    | None -> []
                    | Some(loadClosure) -> 
                        loadClosure.References
                            |> List.map snd
                            |> List.concat 
                            |> List.filter(fun ar->IsPosMatch(pos, ar.originalReference))
                match matches with 
                | resolved::_ // Take the first seen
                | [resolved] -> 
                    let tip =                 
                        match resolved.resolvedFrom with 
                        | AssemblyFolders ->
                            LineIfExists(resolved.resolvedPath)
                            + LineIfExists(resolved.fusionName)
                            + (FSComp.SR.assemblyResolutionFoundByAssemblyFoldersKey())
                        | AssemblyFoldersEx -> 
                            LineIfExists(resolved.resolvedPath)
                            + LineIfExists(resolved.fusionName)
                            + (FSComp.SR.assemblyResolutionFoundByAssemblyFoldersExKey())
                        | TargetFrameworkDirectory -> 
                            LineIfExists(resolved.resolvedPath)
                            + LineIfExists(resolved.fusionName)
                            + (FSComp.SR.assemblyResolutionNetFramework())
                        | Unknown 
                        | RawFileName -> 
                            LineIfExists(resolved.fusionName)
                        | GlobalAssemblyCache -> 
                            LineIfExists(resolved.fusionName)
                            + (FSComp.SR.assemblyResolutionGAC())+ "\n"
                            + LineIfExists(resolved.redist)
                        | Path _ ->
                            LineIfExists(resolved.resolvedPath)
                            + LineIfExists(resolved.fusionName)  
                            
                                                  
                    DataTipText( [DataTipElement(tip.TrimEnd([|'\n'|]) ,XmlCommentNone)])
                | [] -> DataTipText([]))
            (fun err -> DataTipText([DataTipElementCompositionError err]))

    // GetDataTipText: return the "pop up" (or "Quick Info") text given a certain context.
    member x.GetDataTipText line lineStr colAtEndOfNames names : DataTipText  = 
        use t = Trace.Call("CompilerServices","GetDataTipText", fun _->sprintf " line=%+A,idx=%+A,names=%+A" line colAtEndOfNames names)
        
        let Compute() = 
            ErrorScope.Protect 
                Range.range0 
                (fun () -> 
                    match GetDeclItemsForNamesAtPosition(Some(names),None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToTypeRefs) with
                    | None -> DataTipText([])
                    | Some(items,denv,m) ->
                         DataTipText(items |> List.map (FormatDescriptionOfItem infoReader m denv )))
                (fun err -> DataTipText([DataTipElementCompositionError err]))
               
        // See devdiv bug 646520 for rationale behind truncating and caching these quick infos (they can be big!)
        let key = line,colAtEndOfNames,lineStr
        match getDataTipTextCache.TryGet(key) with 
        | Some(res) -> res
        | None ->
             let res = Compute()
             getDataTipTextCache.Put(key,res)
             res

    member x.GetF1Keyword line lineStr colAtEndOfNames names : string option =
       use t = Trace.Call("CompilerServices","GetF1Keyword", fun _->sprintf " line=%+A,idx=%+A,names=%+A" line colAtEndOfNames names) 
       ErrorScope.Protect
            Range.range0
            (fun () ->
                match GetDeclItemsForNamesAtPosition(Some(names),None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToTypeRefs) with
                | None -> None
                | Some(items,_,_) ->
                    match items with
                    | [] -> None
                    | [item] ->
                        GetF1Keyword item                        
                    | _ ->
                        // handle new Type()
                        let allTypes, constr =
                            List.fold (fun (allTypes,constr) item ->
                                match item, constr with
                                |   (Item.Types _), _ -> allTypes, constr
                                |   (Item.CtorGroup _), None -> allTypes, Some item
                                |   _ -> false, None) (true,None) items
                        match allTypes, constr with
                        |   true, Some item -> GetF1Keyword item                        
                        |   _ -> None
            )    
            (fun _ -> None)
                        
    member scope.GetMethods line lineStr colAtEndOfNames namesOpt : MethodOverloads =
        ErrorScope.Protect 
            Range.range0 
            (fun () -> 
                use t = Trace.Call("CompilerServices", "GetMethods", fun _ -> sprintf "line = %d, idx = %d, names = %+A" line colAtEndOfNames namesOpt)
                match GetDeclItemsForNamesAtPosition(namesOpt,None,line,lineStr,colAtEndOfNames,ResolveTypeNamesToCtors) with
                | None -> MethodOverloads("",[| |])
                | Some(items,denv,m) -> MethodOverloads.Create(infoReader,m,denv,items))
            (fun msg -> 
                MethodOverloads(msg,[| |]))

    member scope.GetDeclarationLocationInternal (forceFsiGeneration : bool)(line : int)(lineStr : string)(idx : int)(names : Names)(tag : tokenId option)(isDecl : bool) : FindDeclResult =
      // Get the fully-qualified name of a `namedItemInEnv`
      //
      // This code relies on the fact that the pretty printing code in tastops.ml has been augmented (in subtle ways) 
      // to attach "mangled paths" to each item that is pretty printed. These paths
      // are then attached as attributes to the layout object generated by the pretty printing. A somewhat baroque piece of 
      // code in FsiGeneration.Renderer (posTrackMappingBuildingR) consumes these attributes during the rendering process
      // and uses them to build up a dicitonary that tracks the "path --> line number" mapping as the printing proceeds. 
      // This dictionary is saved. When queried (here), the paths are re-generated using 'fullMangledNameOfItemForFsiGeneration', and 
      // this path is used as a key into the dictionary. 
      //
      // Thus the behaviour of fullMangledNameOfItemForFsiGeneration must patch the mangled path generation during the recursive-decent pretty printing.
      //
      // It would be better to get remove these pseudo-unique paths altogether and instead simply
      // attach the stamp of the item via a (non-string) attribute to the pretty printing layout object and 
      // do the lookup based on the unique integer stamp. However this would require
      //    (a) rely on having unique stamps for:
      //         - each Abstract IL declaration entity (ILTypeDef, ILMethodDef, ILFieldDef etc.)
      //         - each UnionCase and RecordField declaration entity
      //        (both would be "Good" to have for other reasons, e.g. get rid of pointer equality on these elements in several places)
      //    (b) require us to change the attribution object on layouts to "obj" or some other generic type
      
      let rec fullMangledNameOfItemForFsiGeneration x =
          let stringName =
              match x with
              | Item.Value v                             -> approxFullMangledNameOfValRef v
              | Item.UnionCase (UnionCaseInfo(_,v))      -> approxFullMangledNameOfUnionCaseRef v
              | Item.ExnCase v                           -> approxFullMangledNameOfExnRef v
              | Item.RecdField (RecdFieldInfo (_, v))  -> approxFullMangledNameOfRecdFieldRef v
              | Item.Types (_, (TType_app (v, _)) :: _) -> approxFullMangledNameOfTyconRef v
              | Item.ModuleOrNamespaces (v :: _)                  -> approxFullMangledNameOfModuleOrNamespaceRef v
              | Item.MethodGroup (_, meth :: _)         -> 
                  match meth with // grab just the first overload
                  | FSMeth (_,_, v,_)                    -> approxFullMangledNameOfValRef v
                  | ILMeth (_,ILMethInfo (t, _, i, _),_) -> 
                      let methName  = i.Name // this is the name only of the method, so we'll have to look up the class's full name also
                      let className = Item.Types ("", [ t.ToType ]) |> fullMangledNameOfItemForFsiGeneration
                      className @ [ methName ] |> PrettyNaming.JoinNamesForFsiGenerationPath
                  | _  -> ""
              | Item.ILField (ILFieldInfo (ti, fd))   -> 
                  let className = Item.Types ("", [ ti.ToType ]) |> fullMangledNameOfItemForFsiGeneration
                  className @ [fd.Name] |> PrettyNaming.JoinNamesForFsiGenerationPath
              | _                                      -> ""
          stringName |> PrettyNaming.SplitNamesForFsiGenerationPath |> PrettyNaming.ChopUnshowableInFsiGenerationPath

      match tag with
      | None // we have to be charitable in this case -- this makes `scope.GetDeclarationLocation` backward-compatible
      | Some TOKEN_IDENT -> 
          match GetDeclItemsForNamesAtPosition (Some(names), None, line, lineStr, idx, ResolveTypeNamesToTypeRefs) with
          | None
          | Some ([], _, _)         -> FindDeclResult.IdNotFound
          | Some (h :: _ , _, _) -> 
              let h' =
                  match h with
                  | Item.MethodGroup (_, (ILMeth (_,ilinfo,_)) :: _) // rangeOfItem, ccuOfItem don't work on IL methods or fields; we'll be okay, though, looking up the method's *type* instead because they've the same CCU / source file
                  | Item.CtorGroup (_, (ILMeth (_,ilinfo,_)) :: _) ->
                      let (ILMethInfo (typeInfo,_,_,_)) = ilinfo
                      Item.Types ("", [ typeInfo.ToType ])
                  | Item.ILField (ILFieldInfo (typeInfo, _)) -> Item.Types ("", [ typeInfo.ToType ])
                  | _                                         -> h
              match rangeOfItem g isDecl h' with
              | None   -> FindDeclResult.IdNotFound
              | Some m -> 
                  if verbose then dprintf "tcConfig.fsharpBinariesDir = '%s'\n" sTcConfig.fsharpBinariesDir

                  let filename       = fileNameOfItem g (Some sProjectDir) m h'

                  let resultInFile   = FindDeclResult.DeclFound ((m.StartLine - 1, m.StartColumn), filename, []) 
                  let generateFsi () =
                      match ccuOfItem g h' with
                      | Some ccu -> 
                          match ccu.FileName with
                          | Some n when !TestHooks.enableFsiGenerationHook -> 
                              // Generation of source from assembly is currently disabled
                              // it can be turned on using the 'enableInterfaceGeneration' flag
                              FindDeclResult.NeedToGenerate (n, Filename.fullpath g.directoryToResolveRelativePaths, fullMangledNameOfItemForFsiGeneration h)
                          | _ -> FindDeclResult.IdNotFound
                      | _        -> FindDeclResult.IdNotFound

                  if forceFsiGeneration
                      then generateFsi ()
                      else if Internal.Utilities.FileSystem.File.SafeExists filename
                              then resultInFile
                              else generateFsi ()
      | _                -> FindDeclResult.IdNotFound

    member scope.GetDeclarationLocation (line:int)(lineStr:string)(idx:int)(names: Names)(isDecl:bool) : FindDeclResult = scope.GetDeclarationLocationInternal false line lineStr idx names None isDecl

//----------------------------------------------------------------------------
// Navigation items. 
//--------------------------------------------------------------------------

type DeclarationItemKind =
|   NamespaceDecl
|   ModuleFileDecl
|   ExnDecl
|   ModuleDecl
|   TypeDecl
|   MethodDecl
|   PropertyDecl
|   FieldDecl
|   OtherDecl

/// Represents an item to be displayed in the navigation bar
[<Sealed>]
type DeclarationItem(uniqueName : string, name : string, kind : DeclarationItemKind, glyph : int, range : range, bodyRange : range, singleTopLevel:bool) = 
    
    let range_of_m (m:range) = ((m.StartColumn, m.StartLine), (m.EndColumn, m.EndLine))
    member x.bodyRange = bodyRange
    
    member x.UniqueName = uniqueName
    member x.Name = name
    member x.Glyph = glyph
    member x.Kind = kind
    member x.Range = range_of_m range
    member x.BodyRange = range_of_m bodyRange 
    member x.IsSingleTopLevel = singleTopLevel
    member x.WithUniqueName(uniqueName : string) =
      DeclarationItem(uniqueName, name, kind, glyph, range, bodyRange, singleTopLevel)
    static member Create(name : string, kind, glyph : int, range : range, bodyRange : range, singleTopLevel:bool) = 
      DeclarationItem("", name, kind, glyph, range, bodyRange, singleTopLevel)

/// Represents top-level declarations (that should be in the type drop-down)
/// with nested declarations (that can be shown in the member drop-down)
[<NoEquality; NoComparison>]
type TopLevelDeclaration = 
    { Declaration : DeclarationItem
      Nested : DeclarationItem[] }
      
/// Represents result of 'GetNavigationItems' operation - this contains
/// all the members and currently selected indices. First level correspond to
/// types & modules and second level are methods etc.
[<Sealed>]
type NavigationItems(declarations:TopLevelDeclaration[]) =
    member x.Declarations = declarations
   
//----------------------------------------------------------------------------
// Untyped scope
//----------------------------------------------------------------------------

[<NoEquality; NoComparison>]
type internal UntypedParseResults = 
  { // Error infos
    Errors : ErrorInfo array
    // Untyped AST
    Input : Input option
    // Do not report errors from the type checker
    StopErrorReporting : bool
    /// When these files change then the build is invalid
    DependencyFiles : string list
    }

/// 
[<Sealed>]
type internal UntypedParseInfo(parsed:UntypedParseResults, syncop:(unit -> unit) -> unit) = 

    let union_ranges_checked r1 r2 = if r1 = range.Zero then r2 elif r2 = range.Zero then r1 else unionRanges r1 r2
    
    let range_of_decls' f decls = 
      match (decls |> List.map (f >> (fun (d:DeclarationItem) -> d.bodyRange))) with 
      | hd::tl -> tl |> List.fold (union_ranges_checked) hd
      | [] -> range.Zero
    
    let range_of_decls = range_of_decls' fst

    let moduleRange (idm:range) others = 
      union_ranges_checked idm.EndRange (range_of_decls' (fun (a, _, _) -> a) others)
    
    let fldspec_range fldspec =
      match fldspec with
      | UnionCaseFields(flds) -> flds |> List.fold (fun st (Field(_, _, _, _, _, _, _, m)) -> union_ranges_checked m st) range.Zero
      | UnionCaseFullType(ty, _) -> ty.Range
      
    let bodyRange mb decls =
      union_ranges_checked (range_of_decls decls) mb
          
    /// Get information for implementation file        
    let getNavigationFromImplFile (name:string) (modules:SynModuleOrNamespace list) =

        // Map for dealing with name conflicts
        let nameMap = ref Map.empty 
        let addItemName name = 
            let count = defaultArg (!nameMap |> Map.tryFind name) 0
            nameMap := (Map.add name (count + 1) (!nameMap))
            (count + 1)
        let uniqueName name idx = 
            let total = Map.find name (!nameMap)
            sprintf "%s_%d_of_%d" name idx total

        // Create declaration (for the left dropdown)                
        let createDeclLid(baseName, lid, kind, baseGlyph, m, bodym, nested) =
            let name = (if baseName <> "" then baseName + "." else "") + (textOfLid lid)
            DeclarationItem.Create
              (name, kind, baseGlyph * 6, m, bodym, false), (addItemName name), nested
            
        let createDecl(baseName, (id:Ident), kind, baseGlyph, m, bodym, nested) =
            let name = (if baseName <> "" then baseName + "." else "") + (id.idText)
            DeclarationItem.Create
              (name, kind, baseGlyph * 6, m, bodym, false), (addItemName name), nested
         
        // Create member-kind-of-thing for the right dropdown
        let createMemberLid(lid, kind, baseGlyph, m) =
            DeclarationItem.Create(textOfLid lid, kind, baseGlyph * 6, m, m, false), (addItemName(textOfLid lid))

        let createMember((id:Ident), kind, baseGlyph, m) =
            DeclarationItem.Create(id.idText, kind, baseGlyph * 6, m, m, false), (addItemName(id.idText))
            

        // Process let-binding
        let processBinding isMember (Binding(_, _, _, _, _, _, SynValData(memebrOpt, _, _), synPat, _, synExpr, _, _)) =
            let m = match synExpr with 
                    | SynExpr.Typed(e, _, _) -> e.Range // fix range for properties with type annotations
                    | _ -> synExpr.Range
            match synPat, memebrOpt with
            | SynPat.LongIdent(lid, _,_, _, _, _), Some(flags) when isMember -> 
                let icon, kind =
                  match flags.MemberKind with
                  | MemberKind.ClassConstructor
                  | MemberKind.Constructor
                  | MemberKind.Member -> 
                        (if flags.IsOverrideOrExplicitImpl then iIconGroupMethod2 else iIconGroupMethod), MethodDecl
                  | MemberKind.PropertyGetSet
                  | MemberKind.PropertySet
                  | MemberKind.PropertyGet -> iIconGroupProperty, PropertyDecl
                let lidShow, rangeMerge = 
                  match lid with 
                  | _thisVar::nm::_ -> (List.tail lid, nm.idRange) 
                  | hd::_ -> (lid, hd.idRange) 
                  | _ -> (lid, m)
                [ createMemberLid(lidShow, kind, icon, unionRanges rangeMerge m) ]
            | SynPat.LongIdent(lid, _,_, _, _, _), _ -> [ createMemberLid(lid, FieldDecl, iIconGroupConstant, unionRanges (List.head lid).idRange m) ]
            | _ -> []
        
        // Process a class declaration or F# type declaration
        let rec processTycon baseName (TypeDefn(ComponentInfo(_, _, _, lid, _, _, _, _), repr, membDefns, m)) =
            let topMembers = processMembers membDefns |> snd
            match repr with
            | SynTypeDefnRepr.ObjectModel(_, membDefns, mb) -> // mb is odd
                // F# class declaration
                let members = processMembers membDefns |> snd
                let nested = members@topMembers
                ([ createDeclLid(baseName, lid, TypeDecl, iIconGroupClass, m, bodyRange mb.StartRange nested, nested) ] : ((DeclarationItem * int * _) list))
            | SynTypeDefnRepr.Simple(simple, _) ->
                // F# type declaration
                match simple with
                | SynTypeDefnSimpleRepr.Union(_, cases, mb) -> // mb has wrong end, we use only start
                    let cases = 
                        [ for (UnionCase(_, id, fldspec, _, _, _)) in cases -> 
                            createMember(id, OtherDecl, iIconGroupValueType, unionRanges (fldspec_range fldspec) id.idRange) ]
                    let nested = cases@topMembers              
                    [ createDeclLid(baseName, lid, TypeDecl, iIconGroupUnion, m, bodyRange mb.StartRange nested, nested) ]
                | SynTypeDefnSimpleRepr.Enum(cases, mb) -> // mb is wrong
                    let cases = 
                        [ for (EnumCase(_, id, _, _, m)) in cases ->
                            createMember(id, FieldDecl, iIconGroupEnumMember, m) ]
                    let nested = cases@topMembers
                    [ createDeclLid(baseName, lid, TypeDecl, iIconGroupEnum, m, bodyRange mb.StartRange nested, nested) ]
                | SynTypeDefnSimpleRepr.Record(_, fields, mb) ->
                    let fields = 
                        [ for (Field(_, _, id, _, _, _, _, m)) in fields do
                            if (id.IsSome) then
                              yield createMember(id.Value, FieldDecl, iIconGroupFieldBlue, m) ]
                    let nested = fields@topMembers
                    [ createDeclLid(baseName, lid, TypeDecl, iIconGroupType, m, bodyRange mb nested, nested) ]                // no StartRange here, records work fine
                | SynTypeDefnSimpleRepr.TypeAbbrev(_, mb) ->
                    [ createDeclLid(baseName, lid, TypeDecl, iIconGroupTypedef, m, bodyRange mb topMembers, topMembers) ]                // should be ok too
                          
                //| SynTypeDefnSimpleRepr.General of TyconKind * (SynType * range * ident option) list * (valSpfn * MemberFlags) list * fieldDecls * bool * bool * range 
                //| SynTypeDefnSimpleRepr.ILAssembly of ILType * range
                //| TyconCore_repr_hidden of range
                | _ -> [] 
                  
        // Returns class-members for the right dropdown                  
        and processMembers members : (range * list<DeclarationItem * int>) = 
            let members = members |> List.map (fun memb ->
               (memb.Range,
                match memb with
                | SynMemberDefn.LetBindings(binds, _, _, _) -> List.collect (processBinding false) binds
                | SynMemberDefn.Member(bind, _) -> processBinding true bind
                | SynMemberDefn.ValField(Field(_, _, Some(rcid), ty, _, _, _, _), _) ->
                    [ createMember(rcid, FieldDecl, iIconGroupFieldBlue, ty.Range) ]
                | SynMemberDefn.AbstractSlot(ValSpfn(_, id, _, ty, _, _, _, _, _, _, _), _, _) ->
                    [ createMember(id, MethodDecl, iIconGroupMethod2, ty.Range) ]
                | SynMemberDefn.NestedType _ -> failwith "tycon as member????" //processTycon tycon                
                | SynMemberDefn.Interface(_, Some(membs), _) ->
                    processMembers membs |> snd
                | _ -> []  )) 
            ((members |> Seq.map fst |> Seq.fold union_ranges_checked range.Zero),
             (members |> List.map snd |> List.concat))

        // Process declarations in a module that belong to the right drop-down (let bindings)
        let processNestedDeclarations decls = decls |> List.collect (function
            | SynModuleDecl.Let(_, binds, _) -> List.collect (processBinding false) binds
            | _ -> [] )        

        // Process declarations nested in a module that should be displayed in the left dropdown
        // (such as type declarations, nested modules etc.)                            
        let rec processTopLevelDeclarations(baseName, decls) = decls |> List.collect (function
            | SynModuleDecl.ModuleAbbrev(id, lid, m) ->
                [ createDecl(baseName, id, ModuleDecl, iIconGroupModule, m, rangeOfLid lid, []) ]
                
            | SynModuleDecl.NestedModule(ComponentInfo(_, _, _, lid, _, _, _, _), decls, _, m) ->                
                // Find let bindings (for the right dropdown)
                let nested = processNestedDeclarations(decls)
                let newBaseName = (if (baseName = "") then "" else baseName+".") + (textOfLid lid)
                
                // Get nested modules and types (for the left dropdown)
                let other = processTopLevelDeclarations(newBaseName, decls)
                createDeclLid(baseName, lid, ModuleDecl, iIconGroupModule, m, union_ranges_checked (range_of_decls nested) (moduleRange (rangeOfLid lid) other), nested)::other
                  
            | SynModuleDecl.Types(tydefs, _) -> tydefs |> List.collect (processTycon baseName)                                    
                            
            | SynModuleDecl.Exception(ExceptionDefn(ExceptionDefnRepr(_, (UnionCase(_, id, fldspec, _, _, _)), _, _, _, _), membDefns, _), m) ->
                // Exception declaraton
                let nested = processMembers membDefns |> snd
                [ createDecl(baseName, id, ExnDecl, iIconGroupException, m, fldspec_range fldspec, nested) ] 
            | _ -> [] )            
                  
        // Collect all the items  
        let items = 
            // Show base name for this module only if it's not the root one
            let singleTopLevel = (modules.Length = 1)
            modules |> List.collect (fun (ModuleOrNamespace(id,isModule,decls,_,_,_,m)) ->
                let baseName = if (not singleTopLevel) then textOfLid id else ""
                // Find let bindings (for the right dropdown)
                let nested = processNestedDeclarations(decls)
                // Get nested modules and types (for the left dropdown)
                let other = processTopLevelDeclarations(baseName, decls)
                
                // Create explicitly - it can be 'single top level' thing that is hidden
                let decl =
                    DeclarationItem.Create
                        (textOfLid id, (if isModule then ModuleFileDecl else NamespaceDecl),
                            iIconGroupModule * 6, m, 
                            union_ranges_checked (range_of_decls nested) (moduleRange (rangeOfLid id) other), 
                            singleTopLevel), (addItemName(textOfLid id)), nested
                decl::other )
                  
        let items = 
            items 
            |> Array.ofList 
            |> Array.map (fun (d, idx, nest) -> 
                let nest = nest |> Array.ofList |> Array.map (fun (decl, idx) -> decl.WithUniqueName(uniqueName d.Name idx))
                nest |> Array.sortInPlaceWith (fun a b -> compare a.Name b.Name)
                { Declaration = d.WithUniqueName(uniqueName d.Name idx); Nested = nest } )                  
        items |> Array.sortInPlaceWith (fun a b -> compare a.Declaration.Name b.Declaration.Name)
        new NavigationItems(items)

    member scope.Results = parsed
    
    /// Get declaraed items and the selected item at the specified location
    member private scope.GetNavigationItemsImpl() =
       ErrorScope.Protect 
            Range.range0 
            (fun () -> 
                use t = Trace.Call("CompilerServices", "GetNavigationItems", fun _ -> "")
                match parsed.Input with
                | Some(ImplFileInput(ImplFile(modname,_isScript,_qualName,_pragmas,_hashDirectives,modules,_isLastCompiland))) ->
                    getNavigationFromImplFile modname modules 
                | Some(SigFileInput(SigFile(_modname,_qualName,_pragmas,_hashDirectives,_modules))) ->
                    new NavigationItems([| |])
                | _ -> 
                    new NavigationItems([| |]) )
            (fun _ -> new NavigationItems([| |]))   
            
    member private scope.ValidateBreakpointLocationImpl((line,col)) =

        
        let pos = mkPos (line+1) col
        let adjust (p:pos) = (p.Line - 1),p.Column 
        let adjustRange (m:range) = adjust m.Start, adjust m.End
        
        // Process let-binding
        let findBreakPoints allowSameLine = 
            let checkRange m = [ if rangeContainsPos m pos || (allowSameLine && m.StartLine = pos.Line) then 
                                     yield adjustRange m ]
            let walkBindSeqPt sp = [ match sp with SequencePointAtBinding m -> yield! checkRange m | _ -> () ]
            let walkForSeqPt sp = [ match sp with SequencePointAtForLoop m -> yield! checkRange m | _ -> () ]
            let walkWhileSeqPt sp = [ match sp with SequencePointAtWhileLoop m -> yield! checkRange m | _ -> () ]
            let walkTrySeqPt sp = [ match sp with SequencePointAtTry m -> yield! checkRange m | _ -> () ]
            let walkWithSeqPt sp = [ match sp with SequencePointAtWith m -> yield! checkRange m | _ -> () ]
            let walkFinallySeqPt sp = [ match sp with SequencePointAtFinally m -> yield! checkRange m | _ -> () ]

            let rec walkBind (Binding(_, _, _, _, _, _, SynValData(memFlagsOpt,_,_), synPat, _, synExpr, _, spInfo)) =
                [ // Don't yield the binding sequence point if there are any arguments, i.e. we're defining a function or a method
                  let isFunction = 
                      isSome memFlagsOpt ||
                      match synPat with 
                      | SynPat.LongIdent (_,_,_,args,_,_) when nonNil args -> true
                      | _ -> false
                  if not isFunction then 
                      yield! walkBindSeqPt spInfo

                  yield! walkExpr (isFunction || (match spInfo with SequencePointAtBinding _ -> false | _-> true)) synExpr ]

            and walkExprs es = [ for e in es do yield! walkExpr false e ]
            and walkBinds es = [ for e in es do yield! walkBind e ]
            and walkMatchClauses cl = 
                [ for (Clause(_,whenExpr,e,_,_)) in cl do 
                    match whenExpr with Some e -> yield! walkExpr false e | _ -> ()
                    yield! walkExpr true e; ]

            and walkExprOpt (spAlways:bool) eOpt = [ match eOpt with Some e -> yield! walkExpr spAlways e | _ -> () ]
            
            // Determine the breakpoint locations for an expression. spAlways indicates we always
            // emit a breakpoint location for the expression unless it is a syntactic control flow construct
            and walkExpr (spAlways:bool)  e = 
                [ if spAlways && not (IsControlFlowExpression e) then 
                      yield! checkRange e.Range
                  match e with 

                  | SynExpr.ArbitraryAfterError _ 
                  | SynExpr.LongIdent _
                  | SynExpr.Quote _
                  | SynExpr.LibraryOnlyILAssembly _
                  | SynExpr.LibraryOnlyStaticOptimization _
                  | SynExpr.Null _
                  | SynExpr.DeprecatedTypeOf _
                  | SynExpr.Ident _
                  | SynExpr.ImplicitZero _
                  | SynExpr.Const _ -> 
                     ()

                  | SynExpr.TypeTest (e,_,_)
                  | SynExpr.Upcast (e,_,_)
                  | SynExpr.AddressOf (_,e,_,_)
                  | SynExpr.CompExpr (_,_,e,_) 
                  | SynExpr.ArrayOrListOfSeqExpr (_,e,_)
                  | SynExpr.Typed (e,_,_)
                  | SynExpr.DiscardAfterError (e,_) 
                  | SynExpr.Do (e,_)
                  | SynExpr.Assert (e,_)
                  | SynExpr.DotGet (e,_,_) 
                  | SynExpr.LongIdentSet (_,e,_)
                  | SynExpr.New (_,_,e,_) 
                  | SynExpr.TypeApp (e,_,_) 
                  | SynExpr.LibraryOnlyUnionCaseFieldGet (e,_,_,_) 
                  | SynExpr.Downcast (e,_,_)
                  | SynExpr.InferredUpcast (e,_)
                  | SynExpr.InferredDowncast (e,_)
                  | SynExpr.Lazy (e, _)
                  | SynExpr.TraitCall(_,_,e,_)
                  | SynExpr.Paren(e,_) -> 
                      yield! walkExpr false e

                  | SynExpr.YieldOrReturn (_,e,_)
                  | SynExpr.YieldOrReturnFrom (_,e,_)
                  | SynExpr.DoBang  (e,_) ->
                      yield! checkRange e.Range
                      yield! walkExpr false e

                  | SynExpr.NamedIndexedPropertySet (_,e1,e2,_)
                  | SynExpr.DotSet (e1,_,e2,_)
                  | SynExpr.LibraryOnlyUnionCaseFieldSet (e1,_,_,e2,_)
                  | SynExpr.App (_,e1,e2,_) -> 
                      yield! walkExpr false e1; 
                      yield! walkExpr false e2;

                  | SynExpr.ArrayOrList (_,es,_)
                  | SynExpr.Tuple (es,_) -> 
                      yield! walkExprs es

                  | SynExpr.Record (_,_,fs,_) -> 
                      yield! walkExprs (List.map snd fs)

                  | SynExpr.ObjExpr (_,_,bs,is,_) -> 
                      yield! walkBinds bs ; 
                      for (InterfaceImpl(_,bs,_)) in is do yield! walkBinds bs
                  | SynExpr.While (spWhile,e1,e2,_) -> 
                      yield! walkWhileSeqPt spWhile
                      yield! walkExpr false e1; 
                      yield! walkExpr true e2;
                  | SynExpr.For (spFor,_,e1,_,e2,e3,_) -> 
                      yield! walkForSeqPt spFor
                      yield! walkExpr false e1; 
                      yield! walkExpr true e2; 
                      yield! walkExpr true e3;
                  | SynExpr.ForEach (spFor,_,_,e1,e2,_) ->
                      yield! walkForSeqPt spFor
                      yield! walkExpr false e1; 
                      yield! walkExpr true e2; 
                  | SynExpr.Lambda (_,_,_,e,_) -> 
                      yield! walkExpr true e; 
                  | SynExpr.Match (spBind,e,cl,_,_) ->
                      yield! walkBindSeqPt spBind
                      yield! walkExpr false e; 
                      for (Clause(_,whenExpr,e,_,_)) in cl do 
                          yield! walkExprOpt false whenExpr
                          yield! walkExpr true e; 
                  | SynExpr.LetOrUse (_,_,bs,e,_) -> 
                      yield! walkBinds bs ; 
                      yield! walkExpr true e;

                  | SynExpr.TryWith (e,_,cl,_,_,spTry,spWith) -> 
                      yield! walkTrySeqPt spTry
                      yield! walkWithSeqPt spWith
                      yield! walkExpr true e 
                      yield! walkMatchClauses cl
                  
                  | SynExpr.TryFinally (e1,e2,_,spTry,spFinally) ->
                      yield! walkExpr true e1
                      yield! walkExpr true e2
                      yield! walkTrySeqPt spTry
                      yield! walkFinallySeqPt spFinally
                  | SynExpr.Seq (spSeq,_,e1,e2,_) -> 
                      yield! walkExpr (match spSeq with SuppressSequencePointOnStmtOfSequential -> false | _ -> true) e1
                      yield! walkExpr (match spSeq with SuppressSequencePointOnExprOfSequential -> false | _ -> true) e2
                  | SynExpr.IfThenElse (e1,e2,e3opt,spBind,_,_) ->
                      yield! walkBindSeqPt spBind
                      yield! walkExpr false e1
                      yield! walkExpr true e2
                      yield! walkExprOpt true e3opt
                  | SynExpr.DotIndexedGet (e1,es,_,_) -> 
                      yield! walkExpr false e1; 
                      yield! walkExprs es;

                  | SynExpr.DotIndexedSet (e1,es,e2,_,_) ->
                      yield! walkExpr false e1; 
                      yield! walkExprs es;
                      yield! walkExpr false e2; 
                  | SynExpr.DotNamedIndexedPropertySet (e1,_,e2,e3,_) ->
                      yield! walkExpr false e1; 
                      yield! walkExpr false e2; 
                      yield! walkExpr false e3; 

                  | SynExpr.LetOrUseBang  (spBind,_,_,e1,e2,_) -> 
                      yield! walkBindSeqPt spBind
                      yield! walkExpr true e1
                      yield! walkExpr true e2 ]
            
            // Process a class declaration or F# type declaration
            let rec walkTycon (TypeDefn(ComponentInfo(_, _, _, _, _, _, _, _), repr, membDefns, _)) =
                [ for m in membDefns do yield! walkMember m 
                  match repr with
                  | SynTypeDefnRepr.ObjectModel(_, membDefns, _) -> 
                      for m in membDefns do yield! walkMember m 
                  | _ -> () ]
                      
            // Returns class-members for the right dropdown                  
            and walkMember memb  = 
                [ match memb with
                  | SynMemberDefn.LetBindings(binds, _, _, _) -> yield! walkBinds binds
                  | SynMemberDefn.ImplicitCtor(_,_,_,_,m) -> yield! checkRange m
                  | SynMemberDefn.Member(bind, _) -> yield! walkBind bind
                  | SynMemberDefn.Interface(_synty, Some(membs), _) -> for m in membs do yield! walkMember m
                  | SynMemberDefn.Inherit(_, _, m) -> 
                      // can break on the "inherit" clause
                      yield! checkRange m
                  | _ -> ()  ]

            // Process declarations nested in a module that should be displayed in the left dropdown
            // (such as type declarations, nested modules etc.)                            
            let rec walkDecl decl = 
                [ match decl with 
                  | SynModuleDecl.Let(_, binds, m) -> 
                      if rangeContainsPos m pos then 
                          yield! walkBinds binds
                  | SynModuleDecl.DoExpr(spExpr,expr, _) ->  
                      yield! walkBindSeqPt spExpr
                      yield! walkExpr false expr
                  | SynModuleDecl.ModuleAbbrev _ -> 
                      ()
                  | SynModuleDecl.NestedModule(_, decls, _, m) ->                
                      if rangeContainsPos m pos then 
                          for d in decls do yield! walkDecl d
                  | SynModuleDecl.Types(tydefs, m) -> 
                      if rangeContainsPos m pos then 
                          for d in tydefs do yield! walkTycon d
                  | SynModuleDecl.Exception(ExceptionDefn(ExceptionDefnRepr(_, _, _, _, _, _), membDefns, _), m) ->
                      if rangeContainsPos m pos then 
                          for m in membDefns do yield! walkMember m
                  | _ ->
                      () ] 
                      
            // Collect all the items  
            let walkModule (ModuleOrNamespace(_,_,decls,_,_,_,m)) =
                if rangeContainsPos m pos then 
                    [ for d in decls do yield! walkDecl d ]
                else
                    []
                      
           /// Get information for implementation file        
            let walkImplFile (modules:SynModuleOrNamespace list) =
                [ for x in modules do yield! walkModule x ]
                     
            match parsed.Input with
            | Some(ImplFileInput(ImplFile(_,_,_,_,_,modules,_))) -> walkImplFile modules 
            | _ -> []
 
        ErrorScope.Protect 
            Range.range0 
            (fun () -> 
                // Find the last breakpoint reported where the position is inside the region
                match findBreakPoints false |> List.rev with
                | h::_ -> Some(h)
                | _ -> 
                    // If there is no such breakpoint, look for any breakpoint beginning on this line
                    match findBreakPoints true with
                    | h::_ -> Some(h)
                    | _ -> None)
            (fun _msg -> None)   
            
    /// When these files appear or disappear the configuration for the current project is invalidated.
    member scope.DependencyFiles() : string list =
        parsed.DependencyFiles
                    
    member scope.FileName =
      match parsed.Input with
      | Some(ImplFileInput(ImplFile(modname, _, _, _, _, _, _))) 
      | Some(SigFileInput(SigFile(modname, _, _, _, _))) -> modname
      | _ -> ""
    
    // Get items for the navigation drop down bar       
    member scope.GetNavigationItems() =
        use t = Trace.Call("SyncOp","GetNavigationItems", fun _->"")
        // This does not need to be run on the background thread
        scope.GetNavigationItemsImpl()

    member scope.ValidateBreakpointLocation(pos:Position) =
        use t = Trace.Call("SyncOp","ValidateBreakpointLocation", fun _->"")
        // This does not need to be run on the background thread
        scope.ValidateBreakpointLocationImpl(pos)
    
//----------------------------------------------------------------------------
// ParseSource builds all the information necessary to report errors, match braces and build scopes 
//--------------------------------------------------------------------------

module internal Parser = 

    //----------------------------------------------------------------------------
    // Error handling for parsing & type checking
    //--------------------------------------------------------------------------

    type ErrorHandler(reportErrors, mainInputFileName, tcConfig: TcConfig, source: string) =
      let mutable tcConfig = tcConfig
      let mutable errors = false 
      let errorsAndWarningsCollector = new ResizeArray<_>()
      let mutable errorCount = 0
      let mutable errorsAreOn = false 
      let mutable errorsAreFullyOff = false 
         
      // We'll need number of lines for adjusting error messages at EOF
      let fileInfo = 
          (source |> Seq.sumBy (fun c -> if c = '\n' then 1 else 0), // number of lines in the source file
           source.Length - source.LastIndexOf("\n",StringComparison.Ordinal) - 1)             // length of the last line
         
      // This function gets called whenever an error happens during parsing or checking
      let errorSink warn (exn:PhasedError) = 
          // Sanity check here. The phase of an error should be in a phase known to the language service.
          let exn =
            if not(exn.IsPhaseInCompile()) then
                // Reaching this point means that the error would be sticky if we let it prop up to the language service.
                // Assert and recover by replacing phase with one known to the language service.
                System.Diagnostics.Debug.Assert(false, sprintf "The subcategory '%s' seen in an error should not be seen by the language service" (exn.Subcategory()))
                {exn with Phase=BuildPhase.TypeCheck}
            else exn
          if reportErrors then 
              let warn = warn && not (ReportWarningAsError tcConfig.globalWarnLevel tcConfig.specificWarnOff tcConfig.specificWarnOn tcConfig.specificWarnAsError tcConfig.specificWarnAsWarn tcConfig.globalWarnAsError exn)                
              if (errorsAreOn && not errorsAreFullyOff && (not warn || ReportWarning tcConfig.globalWarnLevel tcConfig.specificWarnOff tcConfig.specificWarnOn exn)) then 
                  let oneError trim exn = 
                      // We use the first line of the file as a fallbackRange for reporting unexpected errors.
                      // Not ideal, but it's hard to see what else to do.
                      let fallbackRange = rangeN mainInputFileName 1
                      let ei = ErrorInfo.CreateFromExceptionAndAdjustEof(exn,warn,trim,fallbackRange,fileInfo)
                      Trace.PrintLine("UntypedParseAux", fun _ -> sprintf "Reporting one error: %s\n" (ei.ToString()))
                      errorsAndWarningsCollector.Add ei
                      if not warn then 
                          errors <- true;
                          errorCount <- errorCount + 1
                      
                  let mainError,relatedErrors = Build.SplitRelatedErrors exn 
                  oneError false mainError
                  List.iter (oneError true) relatedErrors
      
      let errorLogger = 
          { new ErrorLogger with 
              member x.WarnSink(exn) = errorSink true exn
              member x.ErrorSink(exn) = errorSink false exn
              member x.ErrorCount = errorCount }
      
      let errorsOn () =  errorsAreOn <- true
      let errorsOff() = errorsAreOn <- false

      // Errors on while parsing project arguments 
      do errorsOn()
      
      
      // Public members
      member x.GetErrorsState() = errorsAreOn
      member x.ErrorLogger = errorLogger
      member x.ErrorSink = errorSink
      member x.PushErrorInfo ei = 
        errorsAndWarningsCollector.Add ei
        errorCount
      member x.CollectedErrorsAndWarnings = errorsAndWarningsCollector.ToArray()
      member x.ErrorCount = errorCount
      member x.TcConfig with set(tc) = tcConfig <- tc
      
      // switch error logging on/off
      member x.ErrorsOn() = errorsOn()
      member x.ErrorsOff() = errorsOff()
      member x.ErrorsFullyOff() = errorsAreFullyOff <- true;
      member x.AnyErrors = errors

    /// Report an unexpect (bug) exception.
    let ReportUnexpectedException(e) = 
        match e with
        | WrappedError(we,_) ->
            System.Diagnostics.Debug.Assert(false, sprintf "Bug seen in service-level request. Underlying wrapped exception was %s\n"  (we.ToString()))
            Trace.PrintLine("CompilerServices", fun _ -> sprintf "Underlying wrapped exception was %s\n" (we.ToString()))
        | _ -> ()
        System.Diagnostics.Debug.Assert(false, sprintf "Bug seen in service-level request: %s\n"  (e.ToString()))
        Trace.PrintLine("CompilerServices", fun _ -> sprintf "Unexpected error %s\n" (e.ToString()))
                
    //----------------------------------------------------------------------------
    // Parsing
    //--------------------------------------------------------------------------

    let ParseSource (source: string, matchBracesOnly:bool, reportErrors:bool, mainInputFileName:string, projectSourceFiles : string list, tcConfig : TcConfig)
          : 
            ( 
              // Error infos
              ErrorInfo array * 
              // Brace matching
              (int * int * int * int * int * int * int * int) array (* MatchPair*) * 
              // Untyped AST
              Input option * 
              // Any errors during parsing
              bool              
            ) = 

      try 
        Trace.PrintLine("CompilerServices", fun _ -> sprintf "Service.parseSource %s, matchBraces = %b, reportErrors = %b" mainInputFileName matchBracesOnly reportErrors)

        // Initialize the error handler 
        let errHandler = new ErrorHandler(reportErrors, mainInputFileName, tcConfig, source)

        // This helps reason=MethodTip to work - todo: investigate why.  reason=MethodTip 
        // calls with only partial text.  Preumably adding this causes the final EndParameters 
        // call to refer to a different line than the StartParameters call we're really interested in 
        // Or something like that.  Yuck. 
        let source = source + "\n\n\n"
        let lexbuf = UnicodeLexing.StringAsLexbuf source

        let adjust (p:pos) = (p.Line - 1),p.Column 
        
        let matchPairRef = new ResizeArray<_>()
        let matchPairSink (m1:range) (m2:range) =  
            if matchBracesOnly then 
                let s1,s2 = adjust m1.Start
                let s3,s4 = adjust m1.End
                let s5,s6 = adjust m2.Start
                let s7,s8 = adjust m2.End
                Trace.PrintLine("MatchPairs", fun _ -> sprintf "MatchPair: (%s,%s)\n" (stringOfRange m1) (stringOfRange m2));
                matchPairRef.Add (s1,s2,s3,s4,s5,s6,s7,s8)

        use unwindEL = PushErrorLoggerPhaseUntilUnwind(fun _oldLogger -> errHandler.ErrorLogger)
        use unwindBP = PushThreadBuildPhaseUntilUnwind (BuildPhase.Parse)

        (* Errors on while parsing project arguments *)            

        let parseResult = 
          if verbose then dprintf "Parsing, text = \n<<<\n%s\n>>>\n" source; 

          // If we're editing a script then we define INTERACTIVE otherwise COMPILED. Since this parsing for intellisense we always
          // define EDITING
          let conditionalCompilationDefines =
            SourceFile.AdditionalDefinesForUseInEditor(mainInputFileName) @ tcConfig.conditionalCompilationDefines 
        
          (* Note: there is currently no way to override this *)
          
          let lightSyntaxStatusInital = tcConfig.ComputeLightSyntaxInitialStatus mainInputFileName
          let lightSyntaxStatus = LightSyntaxStatus(lightSyntaxStatusInital,true)

          // Note: we don't really attempt to intern strings across a large scope
          let lexResourceManager = new Lexhelp.LexResourceManager()
          let lexargs = mkLexargs((fun () -> tcConfig.implicitIncludeDir),
                                  mainInputFileName,
                                  conditionalCompilationDefines,
                                  lightSyntaxStatus,
                                  lexResourceManager,
                                  ref [],
                                  errHandler.ErrorLogger)
          Lexhelp.usingLexbufForParsing (lexbuf, mainInputFileName) (fun lexbuf -> 
              try 
                let skip = true
                let tokenizer = Lexfilter.LexFilter (lightSyntaxStatus, Lexer.token lexargs skip, lexbuf)
                let lexfun = tokenizer.Lexer
                if matchBracesOnly then 
                    (* Quick bracket matching parse  *)
                    let parenTokensBalance t1 t2 = 
                        match t1,t2 with 
                        | (LPAREN,RPAREN) 
                        | (LBRACE,RBRACE) 
                        | (SIG,END) 
                        | (STRUCT,END) 
                        | (LBRACK_BAR,BAR_RBRACK)
                        | (LBRACK,RBRACK)
                        | (BEGIN,END) -> true 
                        | (LQUOTE q1,RQUOTE q2) when q1 = q2 -> true 
                        | _ -> false
                    let rec matchBraces stack = 
                        match lexfun lexbuf,stack with 
                        | tok2,((tok1,m1) :: stack') when parenTokensBalance tok1 tok2-> matchPairSink m1 (Ast.getLexerRange lexbuf); matchBraces stack'
                        | ((LPAREN | LBRACE | LBRACK | LBRACK_BAR | LQUOTE _) as tok),_ -> matchBraces ((tok,Ast.getLexerRange lexbuf) :: stack)
                        | (EOF _ | LEX_FAILURE _),_ -> ()
                        | _ -> matchBraces stack

                    matchBraces [];
                    None
                else 
                    let isLastCompiland = 
                        tcConfig.target.IsExe && 
                        projectSourceFiles.Length >= 1 && 
                        System.String.Compare(List.last projectSourceFiles,mainInputFileName,StringComparison.CurrentCultureIgnoreCase)=0
                    let isLastCompiland = isLastCompiland || Build.IsScript(mainInputFileName)  

                    let parseResult = ParseInput(lexfun,errHandler.ErrorLogger,lexbuf,None,mainInputFileName,isLastCompiland)
                    Some(parseResult)
              with e -> 
                Trace.PrintLine("CompilerServices", fun _ -> sprintf "Could not recover from errors while parsing: %s\n" (e.ToString()))
                errHandler.ErrorLogger.ErrorR(e)
                None)
                
        Trace.PrintLine("CompilerServices", fun _ -> sprintf "#errors = %d\n" errHandler.CollectedErrorsAndWarnings.Length);

        errHandler.CollectedErrorsAndWarnings,
        matchPairRef.ToArray(),
        parseResult,
        errHandler.AnyErrors
        (* unwindEL, unwindBL dispose *)
      with 
      | e -> 
        ReportUnexpectedException(e)
        reraise()

    type TypecheckResultsSink() =
        let capturedEnvs = new ResizeArray<_>(100)
        let capturedExprTypings = new ResizeArray<_>(100)
        let capturedNameResolutions = new ResizeArray<_>(100)
        interface Nameres.ITypecheckResultsSink with
            member sink.NotifyEnvWithScope(scopem,nenv,ad) = capturedEnvs.Add((scopem,nenv,ad)) 
            member sink.NotifyExprHasType(endPos,ty,denv,nenv,ad,m) = capturedExprTypings.Add((endPos,ty,denv,nenv,ad,m))
            member sink.NotifyNameResolution(endPos,item,occurenceType,denv,nenv,ad,m) = capturedNameResolutions.Add((endPos,item,occurenceType,denv,nenv,ad,m)) 
        member x.CapturedEnvs = capturedEnvs
        member x.CapturedExprTypings = capturedExprTypings
        member x.CapturedNameResolutions = capturedNameResolutions

    let InstallTypecheckResultsSink sink = 
        Nameres.GlobalTypecheckResultsSink := Some sink
        { new IDisposable with member x.Dispose() = Nameres.GlobalTypecheckResultsSink := None }

    //----------------------------------------------------------------------------
    // Type-checking
    //--------------------------------------------------------------------------

    let typeCheckSource
          (parseResult: Input option)
          (source: string) 
          (mainInputFileName: string)
          (projectFileName: string)
          (tcConfig: TcConfig)
          (tcGlobals: TcGlobals)
          (tcImports: TcImports)
          (tcState: TcState)
          (loadClosure: LoadClosure option)
          (backgroundErrors: (PhasedError * bool) list)    // These are the errors and warnings seen by the background compiler for the entire antecedant 
          (syncop: (unit->unit)->unit)
          (isResultObsolete: unit->bool)
          (suppressTypecheckErrors : bool)
          : (ErrorInfo array * Scope option * (*aborted*)bool) = 
      try
        let projectDir = Filename.directoryName (if projectFileName = "" then mainInputFileName else projectFileName)
        Trace.PrintLine("CompilerServices", fun _ -> sprintf "Service.typeCheckSource %s, projectDir = %s" mainInputFileName projectDir)
        match parseResult with 
        // When processing the following cases, we don't need to type-check
        | None -> [| |], None, (*aborted*)false
               
        // Run the type checker...
        | Some parsedMainInput ->

            // Initialize the error handler 
            let errHandler = new ErrorHandler(true,mainInputFileName,tcConfig, source)

            use unwindEL = PushErrorLoggerPhaseUntilUnwind (fun _oldLogger -> errHandler.ErrorLogger)
            use unwindBP = PushThreadBuildPhaseUntilUnwind (BuildPhase.TypeCheck)      
        
            // Apply nowarns to tcConfig (may generate errors, so ensure errorLogger is installed)
            let tcConfig = ApplyNoWarnsToTcConfig tcConfig parsedMainInput           
                    
            // update the error handler with the modified tcConfig
            errHandler.TcConfig <- tcConfig

            // If additional references were brought in by the preprocessor then we need to process them
            match loadClosure with
            | Some(loadClosure) ->
                // If there was a loadClosure, replay the errors and warnings
                loadClosure.RootErrors |> List.iter errorSink
                loadClosure.RootWarnings |> List.iter warnSink
                
                // Create single errors for the #load-ed files.
                //  group errors and warnings by file name
                let errorsGroupedByFileName = 
                                        backgroundErrors 
                                        |> List.map(fun err -> (match RangeOfError (fst err) with Some m-> m.FileName | None -> null),err) 
                                        |> List.GroupByFirst  // fileWithErrors, error list
                //  walk the list of #loads and keep the ones for this file.
                let isRangeInThisFile (m:range) = 0 = String.Compare(mainInputFileName, m.FileName, StringComparison.OrdinalIgnoreCase)
                let hashLoadsInFile = loadClosure.SourceFiles 
                                      |> List.map(fun (loadedFilename,ms)->loadedFilename, ms |> List.filter isRangeInThisFile)
                                      |> List.filter(fun (_,ms) -> ms<>[]) // #loaded file, ranges of #load

                //  join the sets and report errors. 
                //  It is by-design that these messages are only present in the language service. A true build would report the errors at their
                //  spots in the individual source files.
                for hashLoadInFile in hashLoadsInFile do
                    for errorGroupedByFileName in errorsGroupedByFileName do
                        if 0 = String.Compare(fst hashLoadInFile, fst errorGroupedByFileName, StringComparison.OrdinalIgnoreCase) then
                            for rangeOfHashLoad in snd hashLoadInFile do // Handle the case of two #loads of the same file
                                let errorsAndWarnings = snd errorGroupedByFileName |> List.map(fun (pe,f)->pe.Exception,f) // Strip the build phase here. It will be replaced, in total, with TypeCheck
                                let errors,warnings = errorsAndWarnings |> List.partition snd 
                                let errors,warnings = errors |> List.map fst, warnings |> List.map fst
                                
                                let message = HashLoadedSourceHasIssues(warnings,errors,rangeOfHashLoad)
                                if errors=[] then warning(message)
                                else errorR(message)
                | None -> 
                    // For non-scripts, check for disallow #r and #load.
                    ApplyMetaCommandsFromInputToTcConfig tcConfig parsedMainInput |> ignore            
                

            // If we got real errors during parsing don;t report any more errors at all, but typecheck for intellisense 
            if suppressTypecheckErrors then errHandler.ErrorsFullyOff()

            if verbose then 
                tcConfig.includes |> List.iter (fun p -> Trace.PrintLine("CompilerServicesVerbose", fun _ -> sprintf "include directory '%s'\n" p)) ;
                tcConfig.implicitOpens |> List.iter (fun p -> Trace.PrintLine("CompilerServicesVerbose", fun _ -> sprintf "implicit open '%s'\n" p)) ;
                tcConfig.referencedDLLs |> List.iter (fun r -> Trace.PrintLine("CompilerServicesVerbose", fun _ -> sprintf "dll from flags '%s'\n" r.Text)) ;
            
            // A problem arises with nice name generation, which really should only 
            // be done in the backend, but is also done in the typechecker for better or worse. 
            // If we don't do this the NNG accumulates data and 
            // we get a memory leak. 
            tcState.NiceNameGenerator.Reset();
            
            // Typecheck the real input.  
            let sink = TypecheckResultsSink()
            
            use unwind2 = InstallTypecheckResultsSink sink

            let amap = tcImports.GetImportMap()
            let tcEnvAtEnd =
                try
                    // Typecheck is potentially a long running operation. We chop it up here with an Eventually continuation and, at each slice, give a chance
                    // for the client to claim the result as obsolete and have the typecheck abort.
                    let computation = TypecheckSingleInputAndFinishEventually((fun () -> errHandler.ErrorCount = 0),tcConfig,tcImports,tcGlobals,None,tcState,parsedMainInput)
                    let ResultNotObsolete() = not(isResultObsolete())
                    match computation |> Eventually.forceWhile ResultNotObsolete with
                    | Some((tcEnvAtEnd,_,_),_) -> Some(tcEnvAtEnd)
                    | None -> None // Means 'aborted'
                with
                | e ->
                    errorR e
                    Some(tcState.TcEnvFromSignatures)
            
            let errors = errHandler.CollectedErrorsAndWarnings
            
            match tcEnvAtEnd with
            | Some(tcEnvAtEnd) ->
                let scope = 
                    new Scope(tcConfig,tcGlobals, amap ,projectDir,mainInputFileName ,
                                sink.CapturedEnvs, tcEnvAtEnd.NameEnv,
                                sink.CapturedExprTypings,
                                sink.CapturedNameResolutions,
                                loadClosure,
                                syncop)     
                errors, Some(scope), (*aborted*)false
            | None -> 
                errors, None, (*aborted*)true
            (* unwindEL, unwindBP, unwind2 dispose here *)
      with 
      | e -> 
        ReportUnexpectedException(e)
        reraise()

open TokenClassifications
open ItemDescriptions



// NOTE: may be better just to move to optional arguments here
type CheckOptions =
    { 
      ProjectFileName: string
      ProjectFileNames: string array
      ProjectOptions: string array
      IsIncompleteTypeCheckEnvironment : bool;
      UseScriptResolutionRules : bool;      
    }
    /// Whether the two parse options refer to the same project.
    static member AreSameProjectName(options1,options2) =
        options1.ProjectFileName = options2.ProjectFileName          
    /// Compare two options sets with respect to the parts of the options that are important to building.
    static member AreSameProject(options1,options2) =
        CheckOptions.AreSameProjectName(options1,options2) &&
        options1.ProjectFileNames = options2.ProjectFileNames &&
        options1.ProjectOptions = options2.ProjectOptions 
    /// Compute the project directory.
    member po.ProjectDirectory 
        with get() = System.IO.Path.GetDirectoryName(po.ProjectFileName)
    override this.ToString() =
        let files =
            let sb = new StringBuilder()
            this.ProjectFileNames |> Array.iter (fun file -> sb.AppendFormat("    {0}\n", file) |> ignore)
            sb.ToString()
        let options =
            let sb = new StringBuilder()
            this.ProjectOptions |> Array.iter (fun op -> sb.AppendFormat("{0} ", op) |> ignore)
            sb.ToString()
        sprintf "CheckOptions(%s)\n  Files:\n%s  Options: %s" this.ProjectFileName files options
 
/// A thin wrapper over a Decls object (we could eventually eleiminate one for the other)
[<Sealed>]
type DeclarationSet(v: Decls) =
    member x.Items = v.Items
    
[<Sealed>]
type TypeCheckInfo(scope: Scope, fsiGens : (string -> string) -> string -> FsiGeneration.FsiGenerationResult, syncop: (unit->unit)->unit) =

    
    let runSyncOp f = 
        let result = ref None
        syncop (fun () -> result := Some(f()))
        Option.get !result

    /// Resolve the names at the given location to a set of declarations
    member info.GetDeclarations((line,colAtEndOfNames),lineStr,names:NamesWithResidue,tokenTag:int) = 
        use t = Trace.Call("SyncOp","GetDeclarations", fun _->sprintf " at=(%d:%d),names=%+A" line colAtEndOfNames names)
        runSyncOp (fun () -> DeclarationSet(scope.GetDeclarations line lineStr colAtEndOfNames names))

    /// Resolve the names at the given location to give a data tip 
    member info.GetDataTipText((x1,x2),lineStr,names:Names,tokenTag:int) : DataTipText = 
        use t = Trace.Call("SyncOp","GetDataTipText", fun _->sprintf " at=(%d:%d),names=%+A tag=%d tokenId=%+A" x1 x2 names tokenTag (tokenTagToTokenId tokenTag))
        match tokenTagToTokenId tokenTag with 
        | TOKEN_IDENT -> 
            runSyncOp (fun () -> scope.GetDataTipText x1 lineStr x2 names)
        | TOKEN_STRING | TOKEN_STRING_TEXT -> 
            runSyncOp (fun () -> scope.GetReferenceResolutionDataTipText(x1,lineStr,x2))        
        | _ -> DataTipText([])

    member info.GetF1Keyword ((line,colAtEndOfNames),lineStr,names) : string option =
        use t = Trace.Call("SyncOp","GetF1Keyword", fun _->sprintf " at=(%d:%d),names=%+A" line colAtEndOfNames names)
        runSyncOp (fun () -> scope.GetF1Keyword line lineStr colAtEndOfNames names)


    // Resolve the names at the given location to a set of methods
    member info.GetMethods((x1,x2):Position,lineStr:string,names:Names option,tokenTag:int) =
        use t = Trace.Call("SyncOp","GetMethods", fun _->sprintf " at=(%d:%d),names=%+A" x1 x2 names)
        runSyncOp (fun () -> scope.GetMethods x1 lineStr x2 names)
            
    member info.GetDeclarationLocationInternal (forceFsiGeneration : bool)((x1, x2) : Position, lineStr:string, names : Names, tokenTag : int, flag : bool) = 
        use t = Trace.Call("SyncOp","GetDeclarationLocation", fun _->sprintf " at=(%d:%d),names=%+A,flag=%+A" x1 x2 names flag)
        let result = ref None
        let GetDeclarationLocation() = 
            let res = scope.GetDeclarationLocationInternal forceFsiGeneration x1 lineStr x2 names (Some (tokenTagToTokenId tokenTag)) flag 
            let res =
                match res with
                | FindDeclResult.NeedToGenerate (s, fnf, nm) -> 
                    // NOTE: This will be called only when 'enableInterfaceGeneration' is set to 'true'
                    assert(!TestHooks.enableFsiGenerationHook)
                    match (fsiGens fnf s) with
                    | Some (f, m, assms) -> 
                        

                        // Search for the item that has specified name as the prefix
                        // This fixes the issue when reported name contains additional items (to be unique)
                        // such as ["Class"; "foo"; "1"; "Static"] and we're looking for ["Class"; "foo"]
                        let rec tryGetValueUsingPrefix (names:ResizeArray<string>) =
                            let ok, point = m.TryGetValue(List.ofSeq names) 
                            if ok then ok, point
                            else 
                                names.RemoveAt(names.Count - 1)
                                tryGetValueUsingPrefix names
                    
                        let ok,point = tryGetValueUsingPrefix (ResizeArray.ofList nm)
                        if ok then 
                            let x,y = point
                            FindDeclResult.DeclFound ((y, x), f, assms)
                        else FindDeclResult.DeclFound ((0, 0), f, assms) // couldn't find the identifier; let's show the file anyhow
                    | None -> FindDeclResult.NoDeclInfo
                | _ -> res
            result:=Some(res)
        syncop GetDeclarationLocation
        Option.get !result

    /// Resolve the names at the given location to the declaration location of the corresponding construct
    member info.GetDeclarationLocation (p, lineStr, names, tokenTag, flag) = info.GetDeclarationLocationInternal false (p, lineStr, names, tokenTag, flag)

/// Information about the compilation environment    
module internal CompilerEnvironment =
    /// These are the names of assemblies that should be referenced for .fs, .ml, .fsi, .mli files that
    /// are not asscociated with a project
    let DefaultReferencesForOrphanSources = DefaultBasicReferencesForOutOfProjectSources
    
    // Apply command-line arguments.
    let ApplyCommandLineArgumentsToTcConfig(commandLineArgs, tcConfig) =
        try
            ParseCompilerOptions
                (fun _sourceOrDll -> () )
                (Fscopts.GetCoreServiceCompilerOptions tcConfig)
                commandLineArgs             
        with e -> errorRecovery e range0

    /// Create a type-check configuration
    let CreateTcConfig(projectDir,commandLineArgs,useScriptResolutionRules:bool) =  
        let defaultFSharpBinariesDir = 
            match Internal.Utilities.FSharpEnvironment.BinFolderOfDefaultFSharpCompiler with 
                  Some(dir)->dir 
                | None -> System.Environment.GetEnvironmentVariable("mFSharp_BinDir")     
    
        let tcConfigB = Build.TcConfigBuilder.CreateNew(defaultFSharpBinariesDir, true (* optimize for memory *), projectDir) 
        // The following uses more memory but means we don't take read-exclusions on the DLLs we reference 
        // Could detect well-known assemblies--ie System.dll--and open them with read-locks 
        tcConfigB.openBinariesInMemory <- true;
        tcConfigB.resolutionEnvironment 
            <- if useScriptResolutionRules 
                then MSBuildResolver.DesigntimeLike  
                else MSBuildResolver.CompileTimeLike

        ApplyCommandLineArgumentsToTcConfig(commandLineArgs, tcConfigB)

        // Never open PDB files for the language service, even if --standalone is specified
        tcConfigB.openDebugInformationForLaterStaticLinking <- false;
        
        if tcConfigB.framework then
            // ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
            // If you see a failure here running unittests consider whether it it caused by 
            // a mismatched version of Microsoft.Build.Framework. Run unittests under a debugger. If
            // you see an old version of Microsoft.Build.*.dll getting loaded it it is likely caused by
            // using an old ITask or ITaskItem from some tasks assembly.
            // I solved this problem by adding a Unittests.config.dll which has a binding redirect to 
            // the current (right now, 4.0.0.0) version of the tasks assembly.
            // ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
            System.Diagnostics.Debug.Assert(false, "Language service requires --noframework flag")
            tcConfigB.framework<-false
        tcConfigB 
    
    /// Publish compiler-flags parsing logic. Must be fast because its used by the colorizer.
    let GetCompilationDefinesForEditing(filename:string, compilerFlags : string list) =
        let defines = ref(SourceFile.AdditionalDefinesForUseInEditor(filename))
        let MatchAndExtract(flag:string,prefix:string) =
            if flag.StartsWith(prefix) then 
                let sub = flag.Substring(prefix.Length)
                let trimmed = sub.Trim()
                defines := trimmed :: !defines
        let rec QuickParseDefines = function
            | hd :: tail ->
               MatchAndExtract(hd,"-d:")
               MatchAndExtract(hd,"--define:")
               QuickParseDefines tail
            | _ -> ()
        QuickParseDefines compilerFlags
        !defines
            
    /// Return true if this is a subcategory of error or warning message that the language service can emit
    let IsCheckerSupportedSubcategory(subcategory:string) =
        PhasedError.IsSubcategoryOfCompile(subcategory)

/// Information about the debugging environment
module internal DebuggerEnvironment =
    /// Return the language ID, which is the expression evaluator id that the
    /// debugger will use.
    let GetLanguageID() =
        System.Guid(0xAB4F38C9u, 0xB6E6us, 0x43baus, 0xBEuy, 0x3Buy, 0x58uy, 0x08uy, 0x0Buy, 0x2Cuy, 0xCCuy, 0xE3uy)
        
[<Sealed>]
type internal TypeCheckResults(errors: ErrorInfo array,
                               scope: TypeCheckInfo option,
                               untypedInfo : UntypedParseInfo) =
    member pr.Errors = errors
    member pr.TypeCheckInfo = scope
    member pr.UntypedParse = untypedInfo
    
[<NoComparison>]
type internal TypeCheckAnswer =
    | NoAntecedant
    | Aborted
    | TypeCheckSucceeded of TypeCheckResults   
        
/// This file has become eligible to be re-typechecked.
type internal FileTypeCheckStateIsDirty = string -> unit
        
// Identical to _VSFILECHANGEFLAGS in vsshell.idl
type internal DependencyChangeCode =
    | NoChange = 0x00000000
    | FileChanged = 0x00000001
    | TimeChanged = 0x00000002
    | Size = 0x00000004
    | Deleted = 0x00000008
    | Added = 0x00000010        

/// Callback that indicates whether a requested result has become obsolete.    
[<NoComparison;NoEquality>]
type internal IsResultObsolete = 
    | IsResultObsolete of (unit->bool)
        
type BackgroundCompiler(fileTypeCheckStateIsDirty:FileTypeCheckStateIsDirty) =
    let reactor = Reactor.Reactor()
    //------------------------------------------------------------------------------------
    // Rules for reactive building.
    //
    // This phrases the compile as a series of vector functions and vector manipulations.
    //
    // Rules written in this language are then transformed into a plan to execute the 
    // various steps of the process in small parts.
    //-----------------------------------------------------------------------------------
    let CreateIncrementalBuilder (options:CheckOptions) =
        use t = Trace.Call("Reactor","CreateIncrementalBuilder", fun () -> sprintf "options = %+A" options)
        // Trap and report warnings and errors from creation.
        use errorScope = new ErrorScope()

        // Create the builder.         
        let resourceManager = new Lexhelp.LexResourceManager() 
        let sourceFiles = (options.ProjectFileNames|>Array.toList)
        
        let commandLineArgs = options.ProjectOptions |> Array.toList 

        // Build initial TC config
        let tcConfigB = CompilerEnvironment.CreateTcConfig(options.ProjectDirectory, commandLineArgs, options.UseScriptResolutionRules)
        let _, _, assemblyName = tcConfigB.DecideNames sourceFiles
        
        let tcConfig = TcConfig.Create(tcConfigB,validate=true)
        // Share intern'd strings across all lexing/parsing
        let niceNameGen = NiceNameGenerator()
        
        // Sink internal errors and warnings.
        // Q: Why is it ok to ignore these?
        // A: These are errors from the background build of files the user doesn't see. 
        // Squiggles will appear in the editted file via the foreground parse\typecheck
        let warnSink (exn:PhasedError) = Trace.PrintLine("IncrementalBuild", (exn.ToString >> sprintf "Background warning: %s"))
        let errorSink (exn:PhasedError) = Trace.PrintLine("IncrementalBuild", (exn.ToString >> sprintf "Background error: %s"))

        let builder, dependencyFiles = 
            let errorLogger =
                { new ErrorLogger with 
                     member x.ErrorCount=0;
                     member x.WarnSink(e)=warnSink e;
                     member x.ErrorSink(e)=errorSink e }
            IncrementalFSharpBuild.Create (tcConfig, options.ProjectDirectory, assemblyName, niceNameGen,
                                           resourceManager, sourceFiles, 
                                           true, // stay reactive
                                           { BeforeTypeCheckFile = fileTypeCheckStateIsDirty },
                                           errorLogger,
                                           (fun (e:exn)->raise e))
                                 
        Trace.PrintLine("IncrementalBuild", fun () -> sprintf "CreateIncrementalBuilder: %A" dependencyFiles)
#if DEBUG
        dependencyFiles.Files |> List.iter (fun df -> System.Diagnostics.Debug.Assert(System.IO.Path.IsPathRooted(df.Filename), sprintf "dependency file was not absolute: '%s'" df.Filename))
#endif
        (tcConfig, builder, dependencyFiles, errorScope.ErrorsAndWarnings)

    /// Cache of builds keyed by options.        
    let buildCache = MruCache(Flags.buildCacheSize, CreateIncrementalBuilder, areSame =  CheckOptions.AreSameProject, areSameForSubsumption =  CheckOptions.AreSameProjectName)
    
    /// Information about the derived script closure.
    let scriptClosure = AgedLookup<CheckOptions,LoadClosure>(Flags.buildCacheSize, areSame=CheckOptions.AreSameProject)

    /// See if the build cache needs to be invalidated. Return true if there was an invalidation.
    let InvalidateBuildCacheEntry(options) = 
        use t = Trace.Call("ChangeEvents","InvalidateBuildCacheEntry", fun _ -> sprintf "Received notification to invalidate build for options: %A" options)
        match buildCache.GetAvailable(options) with
        | None -> ()
        | Some(_, _, _, _) ->
                Trace.PrintLine("ChangeEvents", fun _ -> "Refreshing configuration")
                let tcConfigB, builderB, dependencyFilesB, errorsB = CreateIncrementalBuilder(options)
                Trace.PrintLine("ChangeEvents", fun _ -> sprintf "CheckOptions(%s) now maps to Build(%s)" (options.ToString()) (builderB.ToString()))
                buildCache.SetAlternate(options, (tcConfigB, builderB, dependencyFilesB, errorsB))


    /// Get the antecedant typecheck state for the give file (in the options). Return none if not available.        
    let GetAntecendantResultWithoutSideEffects(filename:string, options:CheckOptions) = 
        match buildCache.GetAvailable(options) with
        | Some(_,build,dependencyFiles,createErrors) ->
            let slotOfFile = IncrementalFSharpBuild.GetSlotOfFileName(filename, build)
            Some(build, dependencyFiles, createErrors, IncrementalFSharpBuild.GetAntecedentTypeCheckResultsBySlot(slotOfFile,build))
        | None->None        
    
    let GetSlotsCountImpl options syncop =
        Trace.PrintLine("CompilerServices", fun _ -> "Service.GetSlotsCountImpl")
        use t = Trace.CallByThreadNamed("Reactor", "GetSlotsCountImpl", "ThreadPool", fun _->"")  
        let _,build,_,_ = buildCache.Get(options)
        IncrementalFSharpBuild.GetSlotsCount build
        
    
    let UntypedParseForSlotImpl (slot,options) syncop : UntypedParseInfo = 
        Trace.PrintLine("CompilerServices", fun _ -> "Service.UntypedParseForSlotImpl")
        use t = Trace.CallByThreadNamed("Reactor", "UntypedParseImpl", "ThreadPool", fun _->"")  
        let tcConfig,build,dependencyFiles,errors= buildCache.Get(options)
        let inputOpt,_,_,build = IncrementalFSharpBuild.GetParseResultsBySlot(slot,build)            
        Trace.PrintLine("ChangeEvents", fun _ -> sprintf "CheckOptions(%s) now maps to Build(%s)" (options.ToString()) (build.ToString()))
        buildCache.SetAlternate(options, (tcConfig,build,dependencyFiles,errors))
        // Strip everything but the file name.
        let dependencyFiles = dependencyFiles.Files |> List.map (fun dep->dep.Filename)
        
        UntypedParseInfo(parsed = { Errors = [| |]; 
                                    Input = inputOpt;  
                                    StopErrorReporting = false
                                    DependencyFiles = dependencyFiles},
                         syncop = syncop) 

    
    /// Parses the source file and returns untyped AST
    let UntypedParseImpl (filename:string, source,options:CheckOptions) syncop =         
        Trace.PrintLine("CompilerServices", fun _ -> "Service.UntypedParseImpl")
        use t = Trace.CallByThreadNamed("Reactor", "UntypedParseImpl", "ThreadPool", fun _->"")  
        
        let tcConfig,_,dependencyFiles,_ = buildCache.Get(options) // Q: Whis it it ok to ignore createErrors in the build cache? A: These errors will be appended into the typecheck results
            
        // Do the parsing.
        let parseErrors, _matchPairs, inputOpt, anyErrors = 
           Parser.ParseSource (source, false, true, filename, (options.ProjectFileNames |> Array.toList), tcConfig)
                 
        // Strip everything but the file name.
        let dependencyFiles = dependencyFiles.Files |> List.map (fun dep->dep.Filename)

        UntypedParseInfo(parsed = { Errors = parseErrors; 
                                    Input = inputOpt;  
                                    StopErrorReporting = anyErrors
                                    DependencyFiles = dependencyFiles},
                         syncop = syncop) 

    /// Parses the source file and returns untyped AST
    let MatchBracesImpl (filename:string, source,options) syncop =         
        Trace.PrintLine("CompilerServices", fun _ -> "Service.MatchBracesImpl")
        use t = Trace.CallByThreadNamed("Reactor", "MatchBracesImpl", "ThreadPool", fun _->"")  
        let tcConfig,_,_,_ = buildCache.Get(options)
        // Do the parsing.
        let _parseErrors, matchPairs, _inputOpt, _anyErrors = 
           Parser.ParseSource (source, true, false, filename, (options.ProjectFileNames |> Array.toList), tcConfig)
                 
        matchPairs |> Array.map (fun (s1,s2,s3,s4,s5,s6,s7,s8) -> ((s1,s2),(s3,s4)),((s5,s6),(s7,s8)))
        
    /// Do one step of the build for the given options.
    let DoStep(options:CheckOptions) = 
        fun() ->
            // Do the step.
            let tcConfig,last,dependencyFiles,createErrors = buildCache.Get(options)
            let next = IncrementalFSharpBuild.Step(last)

            // Did the step do work?
            match next with
            | Some(next)->
                Trace.PrintLine("ChangeEvents", fun _ -> sprintf "CheckOptions(%s) now maps to Build(%s)" (options.ToString()) (next.ToString()))
                buildCache.SetAlternate(options,(tcConfig,next,dependencyFiles,createErrors))
                true
            | None-> false

    /// Type-check the result obtained by parsing
    /// The input should be first parsed using 'UntypedParseImpl'
    let TypeCheckSourceImpl (parseResult:UntypedParseResults) (filename:string) source (options:CheckOptions) (isResultObsolete:unit->bool) syncop = 
        use t = Trace.CallByThreadNamed("Reactor", "TypeCheckSourceImpl", "ThreadPool", fun _->"")  
        
        // Get additional script #load closure information if applicable.
        let loadClosure = scriptClosure.TryGet(options) // For scripts, this will have been recorded by GetCheckOptionsFromScriptRoot
        
        match GetAntecendantResultWithoutSideEffects(filename,options) with
        | Some(build,dependencies,createErrors,Some(tcPriorState,tcImports,tcGlobals,tcConfig,backgroundErrors,_antecedantTimeStamp)) -> 
        
            dependencies.ImportedCcusInvalidated.Add (fun msg -> 
                System.Diagnostics.Debugger.Log(100, "service", sprintf "A build cache entry is being invalidated because of a : %s" msg)
                reactor.AsyncOp (fun () -> InvalidateBuildCacheEntry(options))
                reactor.StartBuilding(DoStep options))

            // Run the function
            let suppressTypecheckErrors = parseResult.StopErrorReporting 
            let tcErrors, scopeOpt, aborted = 
             Parser.typeCheckSource
                parseResult.Input
                source
                filename
                options.ProjectFileName                    
                tcConfig
                tcGlobals
                tcImports   
                tcPriorState
                loadClosure
                backgroundErrors
                syncop
                isResultObsolete
                suppressTypecheckErrors
                
            if not(aborted) then                           
                // Collect thunks for .fsi generators used by GotoDefinition
                let (generators, _) = IncrementalFSharpBuild.GetFsiGenerators build                       
                                                    
                // Append all the errors together.
                let errors = 
                    [| yield! createErrors; 
                       yield! parseResult.Errors
                       if options.IsIncompleteTypeCheckEnvironment then 
                           yield! Seq.truncate Flags.maxErrorsOutOfProjectContext tcErrors
                       else 
                           yield! tcErrors |]
                
                let res = 
                        TypeCheckResults (errors              = errors,
                                          scope               = (scopeOpt |> Option.map (fun v -> TypeCheckInfo (v, generators, syncop))),
                                          untypedInfo         = new UntypedParseInfo(parseResult, syncop)
                                          )   
                TypeCheckSucceeded(res)
            else Aborted                
        | _ -> 
            // Either the builder did not exist or the antecedent to the slot was not ready. Return 'None'.
            // The caller will send a request for a background build of this project. This
            // will create the builder and notify the UI when the antecedent to the slot is ready. 
            NoAntecedant

    member bc.UntypedParse(inputs)=
        use t = Trace.Call("SyncOp","UntypedParse", fun _->"")
        let result = ref None
        let UntypedParseOp () =
            result:=Some(UntypedParseImpl inputs reactor.SyncOp)
        reactor.SyncOp UntypedParseOp
        Option.get !result
     
    member bc.UntypedParseForSlot(inputs) =
        use t = Trace.Call("SyncOp","UntypedParseForSlot", fun _->"")
        let result = ref None
        let UntypedParseOp () =
            result:=Some(UntypedParseForSlotImpl inputs reactor.SyncOp)
        reactor.SyncOp UntypedParseOp
        Option.get !result
        
    member bc.GetSlotsCount(inputs) =
        use t = Trace.Call("SyncOp","GetSlotsCount", fun _->"")
        let result = ref None
        let GetSlotsCountOp () =
            result:=Some(GetSlotsCountImpl inputs reactor.SyncOp)
        reactor.SyncOp GetSlotsCountOp
        Option.get !result
     
    member bc.MatchBraces(inputs)=
        use t = Trace.Call("SyncOp","MatchBraces", fun _->"")
        let result = ref None
        let MatchBracesOp () =
            result:=Some(MatchBracesImpl inputs reactor.SyncOp)
        reactor.SyncOp MatchBracesOp
        Option.get !result

    member bc.TypeCheckSource(parseSourceRes,filename:string,source,options,isResultObsolete:unit->bool)=
        use t = Trace.Call("SyncOp","TypeCheckSource", fun _->"")
        let result = ref None
        let TypeCheckSourceOp () =
            result:=Some(TypeCheckSourceImpl parseSourceRes filename source options isResultObsolete reactor.SyncOp)
        reactor.SyncOp TypeCheckSourceOp
        Option.get !result

    member bc.GetCheckOptionsFromScriptRoot(filename,source) = 
        let result = ref None
        let GetCheckOptionsFromScriptRootOp () =
            let fas = LoadClosure.FindFromSource(filename, source, (*editing*)true,InteractiveOrCompile.ChooseByFileExtension, new Lexhelp.LexResourceManager())
            let otherFlags = ["--noframework"; "--warn:3"] 
            let references = fas.References |> List.map (fun r->"-r:" + fst r)
            let nowarns = fas.NoWarns |> List.map (fun (code,_)->"--nowarn:" + code)
            let allFlags = otherFlags @ references @ nowarns 
            let co = 
                {
                    ProjectFileName = filename + ".fsproj" // Make a name that is unique in this directory.
                    ProjectFileNames = fas.SourceFiles |> List.map(fun s->fst s) |> List.toArray
                    ProjectOptions = allFlags |> List.toArray
                    IsIncompleteTypeCheckEnvironment = false
                    UseScriptResolutionRules = true 
                }
            scriptClosure.Put(co,fas) // Save the full load closure for later correlation.
            result:=Some(co)
            
        reactor.SyncOp GetCheckOptionsFromScriptRootOp
        Option.get !result            
        
    member bc.InvalidateConfiguration(options : CheckOptions) =
        use t = Trace.Call("SyncOp","InvalidateConfiguration", fun _->"")
        reactor.AsyncOp (fun () -> InvalidateBuildCacheEntry(options))
        reactor.StartBuilding(DoStep options) 

    member bc.StartBuilding(options) =
        reactor.StartBuilding(DoStep options) 

    member bc.StopBuilding() =
        reactor.StopBuilding() 

    member bc.WaitForBackgroundCompile() =
        reactor.WaitForBackgroundCompile() 

[<Sealed>]
[<AutoSerializable(false)>]
type internal InteractiveChecker(fileTypeCheckStateIsDirty) =
    let backgroundCompiler = BackgroundCompiler(fileTypeCheckStateIsDirty)
    static let mutable foregroundParseCount = 0
    static let mutable foregroundTypeCheckCount = 0
    
    /// Determine whether two sets of sources and parse options are the same.
    let AreSameForParsing((f1,s1,o1:CheckOptions),(f2,s2,o2:CheckOptions)) =
        let same = 
            f1 = f2
            && CheckOptions.AreSameProject(o1,o2)
        same && s1 = s2
        
    /// Determine whether two sets of sources and parse options should be subsumed under the same project.
    let AreSubsumableForParsing((_,_,o1:CheckOptions),(_,_,o2:CheckOptions)) =
        CheckOptions.AreSameProjectName(o1,o2)
        
    // Parse using backgroundCompiler
    let ComputeBraceMatching(filename:string,source,options:CheckOptions) = 
        Trace.PrintLine("CompilerServices", fun () -> sprintf "ComputeBraceMatching, FileName = %s\n  " filename) 
        backgroundCompiler.MatchBraces(filename,source,options)


    let braceMatchMru = MruCache<_,_>(Flags.braceMatchCacheSize,ComputeBraceMatching,areSame=AreSameForParsing,areSameForSubsumption=AreSubsumableForParsing,isStillValid=(fun _ -> true)) 

    // /// Cache which holds recently seen parses, up to one for each file, keyed by source text and parse/project options for the file
    // let parseLookup = AgedLookup<(string * CheckOptions),UntypedParseInfo>(8) 
    
    /// Cache which holds recently seen type-checks, no more than one for each file.
    /// This cache may hold out-of-date entries, in two senses
    ///    - there may be a more recent antecedent state available because the background build has made it available
    ///    - the source for the file may have changed
    
    let typeCheckLookup = AgedLookup<string * CheckOptions,UntypedParseInfo * TypeCheckResults *int >(recentForgroundTypeCheckLookupSize,areSame=fun (x,y)->x=y) 

    /// Instantiate an interactive checker.    
    static member Create(fileTypeCheckStateIsDirty) = new InteractiveChecker(fileTypeCheckStateIsDirty)

    /// Parse a source code file, returning an information about the untyped results
    /// and the results needed for further processing using 'TypeCheckSource'
    member ic.MatchBraces(filename: string, source, options) =
        braceMatchMru.Get((filename, source, options))

    /// Parse a source code file, returning an information about the untyped results
    /// and the results needed for further processing using 'TypeCheckSource'
    member ic.UntypedParse(filename: string, source, options) =
        Trace.PrintLine("CompilerServices", fun () -> sprintf "UntypedParse, FileName = %s\n  " filename) 
        foregroundParseCount <- foregroundParseCount + 1
        backgroundCompiler.UntypedParse(filename, source, options)
        
    member ic.GetSlotsCount options =
        Trace.PrintLine("CompilerServices", fun () -> sprintf "GetSlotsCount, ProjectName = %s\n  " options.ProjectFileName)         
        backgroundCompiler.GetSlotsCount(options)
        
    member ic.UntypedParseForSlot (slot,options) =
        Trace.PrintLine("CompilerServices", fun () -> sprintf "UntypedParseForSlot, ProjectName = %s, slot = %d\n  " options.ProjectFileName slot)         
        backgroundCompiler.UntypedParseForSlot(slot,options)
        
    /// Try to get recent approximate type check results for a file. 
    member ic.TryGetRecentTypeCheckResultsForFile(filename: string, options:CheckOptions) =
        match typeCheckLookup.TryGet((filename,options)) with
        | Some res -> 
            Some res
        | _ -> 
            None
            
    /// This function is called when the configuration is known to have changed for reasons not encoded in the CheckOptions.
    /// For example, dependent references may have been deleted or created.
    member ic.InvalidateConfiguration(options: CheckOptions) =
        backgroundCompiler.InvalidateConfiguration(options)         
              
    /// TypeCheck a source code file, returning a handle to the results of the 
    /// parse including the reconstructed types in the file.
    member ic.TypeCheckSource(parsed:UntypedParseInfo,filename:string,fileversion:int,source:string,options:CheckOptions,IsResultObsolete(isResultObsolete)) =        
        Trace.PrintLine("CompilerServices", fun () -> sprintf "TypeCheckSource, FileName = %s\n  " filename) 
        let answer = backgroundCompiler.TypeCheckSource(parsed.Results,filename,source,options,isResultObsolete)
        match answer with 
        | Aborted
        | NoAntecedant ->
            backgroundCompiler.StartBuilding(options) 
            answer
        | TypeCheckSucceeded typedResults -> 
            foregroundTypeCheckCount <- foregroundTypeCheckCount + 1
            typeCheckLookup.Put((filename,options),(parsed,typedResults,fileversion))            
            // JAF: Why don't we kick the backgroundCompiler off here like we do for Aborted and NoAntecedant? 
            // Because we expect the second half of the request (GetMethods or whatever) soon and would like that to have a chance to start that request quickly
            answer
            
    /// For a given script file, get the CheckOptions implied by the #load closure
    member ic.GetCheckOptionsFromScriptRoot(filename : string, source : string) :  CheckOptions = 
        backgroundCompiler.GetCheckOptionsFromScriptRoot(filename,source)
        
    /// Begin background parsing the given project.
    member ic.StartBackgroundCompile(options) = backgroundCompiler.StartBuilding(options) 
    /// Stop the background compile.
    member ic.StopBackgroundCompile() = backgroundCompiler.StopBuilding()
    /// Block until the background compile finishes.
    member ic.WaitForBackgroundCompile() = backgroundCompiler.WaitForBackgroundCompile()

    static member GlobalForegroundParseCountStatistic = foregroundParseCount
    static member GlobalForegroundTypeCheckCountStatistic = foregroundTypeCheckCount

//----------------------------------------------------------------------------
//INDEX: FsiIntelisense
//----------------------------------------------------------------------------

module internal FsiIntelisense =

    let rec getLineIndex (text:string,offset) =
        let crIndex = text.IndexOf("\n",StringComparison.Ordinal)
        if crIndex <> -1 then
            if offset <= crIndex then
                0,offset,text
            else
                let text = text.Substring(crIndex + 1)
                let line,index,text = getLineIndex (text,offset - (crIndex+1))
                line+1,index,text
        else
            0,offset,text

    let getDeclarations (tcConfig, tcGlobals, tcImports, tcState) (text: string)  names =
        let syncop f = f() : unit
        let mockFileName = "stdin.fs" (* Note: build.ml:ParseInput parses differently based on filename extension... *)
        let _, _, inp, suppressTypecheckErrors = Parser.ParseSource (text, false, false, mockFileName, [], tcConfig)
        let _, scopeOpt, _ = Parser.typeCheckSource inp text mockFileName "project" tcConfig tcGlobals tcImports tcState None [] syncop (fun ()->false) suppressTypecheckErrors
        
        match scopeOpt with
          | Some scope when Array.length names > 0 -> 
              let line,index,lineStr = getLineIndex (text,text.Length)                        
              let res = scope.GetDeclarations line lineStr index (List.frontAndBack (List.ofArray names))
              let project i = 
                  let name  = res.Name(i)
                  let desc : string  = match res.Description(i) with DataTipText(DataTipElement(item,_)::_) -> item | _ -> "" 
                  let glyph = res.Glyph(i)
                  let displ = name
                  name,desc,displ,glyph
              let results = Array.init res.Count project
              results
          | _       -> [| |]
          
module internal PrettyNaming =
    let IsIdentifierPartCharacter     = Microsoft.FSharp.Compiler.PrettyNaming.IsIdentifierPartCharacter
    let IsLongIdentifierPartCharacter = Microsoft.FSharp.Compiler.PrettyNaming.IsLongIdentifierPartCharacter
    let GetLongNameFromString         = Microsoft.FSharp.Compiler.PrettyNaming.SplitNamesForFsiGenerationPath
    let FormatAndOtherOverloadsString(remainingOverloads) = FSComp.SR.typeInfoOtherOverloads(remainingOverloads)
        

#if EXTENSIBLE_DUMPER
#if DEBUG

namespace Internal.Utilities.Diagnostic
open Microsoft.FSharp.Compiler.Env
open Microsoft.FSharp.Compiler.Tastops 
open System.Text

type internal typDumper(dumpTarget:Microsoft.FSharp.Compiler.Tast.TType) =
    override self.ToString() = 
        match !global_g with
        | Some(g) -> 
            let denv = DisplayEnv.Empty g
            let sb = StringBuilder()
            NicePrint.outputTy denv sb dumpTarget
            sb.ToString()
        | None -> "No global environment"
    
#endif    
#endif
